project-tiny-context-harness 0.2.52 → 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 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.52.tgz
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, `validate-context` or `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.
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
 
@@ -230,16 +230,48 @@ Use `npx --no-install sdlc-harness ...` only when you explicitly want the alread
230
230
  | Harness upgrade Skill | `<harnessRoot>/skills/context_harness_upgrade/SKILL.md` | Handles explicit Tiny Context / Project Tiny Context Harness upgrade requests such as “upgrade Tiny Context” and “use the Tiny Context upgrade skill to upgrade this project”; it runs `upgrade` first, handles only migration-scoped `manual_required` / `blocked` follow-up, then runs diagnostics. |
231
231
  | Project-local Skills | `<harnessRoot>/skills/<role>/SKILL.md` | Optional local product/design/development Skills created by the project, such as `product_plan`, `uiux_design` or `development_engineer`. They supersede package-managed default Skills when more specific, are not overwritten by `sync`, and should keep front matter trigger keywords aligned with the project `AGENTS.md` role-trigger rule. |
232
232
  | Managed file sync | `make sdlc-sync` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness sync` | Refreshes package-managed guidance, default Skills, Makefile include, context templates, tools and workflow YAML. It does not run migrations or perform semantic Context generation; when migration work is pending it refuses to write and tells you to run `upgrade`. |
233
- | Upgrade | `make sdlc-upgrade` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade` | Default command after updating the npm package. Builds an upgrade plan, applies `safe_pending` migrations, runs `sync` and `doctor`, and exits non-zero when manual or blocked follow-up remains. |
233
+ | Upgrade | `make sdlc-upgrade` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade` | Default command after updating the npm package. Builds an upgrade plan, stops before writes when `blocked` items exist, otherwise applies `safe_pending` migrations, runs `sync` and `doctor`, and exits non-zero when manual follow-up or diagnostics remain. |
234
234
  | Upgrade check | `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade --check [--json]` | Checks the upgrade plan without writing files. Reports `safe_pending`, `manual_required` and `blocked`; exits non-zero when any work remains. |
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
- | 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. |
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`. |
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. |
239
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. |
240
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. |
241
244
 
242
- 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 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.
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.
246
+
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`.
243
275
 
244
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.
245
277
 
@@ -382,7 +414,7 @@ Do not customize the package-managed Harness upgrade Skill directly. Project-spe
382
414
 
383
415
  After updating the package, run `sdlc-harness upgrade`. Use `sync` only when release notes say the update is `sync-only`.
384
416
 
385
- `upgrade` first builds an upgrade plan, applies only `safe_pending` migrations, then runs `sync` and `doctor`. If `manual_required` or `blocked` items remain, the command exits non-zero and prints follow-up. `upgrade --check` performs the same planning step without writing files; `upgrade --check --json` is intended for release checks and CI.
417
+ `upgrade` first builds an upgrade plan. If `blocked` items exist, it prints the plan, runs diagnostics and exits non-zero before migrations or internal `sync`. Without blockers, it applies only `safe_pending` migrations, then runs `sync` and `doctor`. If `manual_required` follow-up or diagnostics remain, the command exits non-zero and prints follow-up. `upgrade --check` performs the same planning step without writing files; `upgrade --check --json` is intended for release checks and CI.
386
418
 
387
419
  Release update modes:
388
420
 
@@ -398,7 +430,7 @@ Migration statuses:
398
430
  |---|---|
399
431
  | `safe_pending` | A known Harness schema, config or path convention can be migrated mechanically. |
400
432
  | `manual_required` | The path is in migration scope, but the Harness cannot prove the right semantic role or user intent. |
401
- | `blocked` | A target conflict or overwrite risk prevents a safe write. |
433
+ | `blocked` | A target conflict or overwrite risk prevents a safe write. Blocked items stop upgrade writes until resolved. |
402
434
 
403
435
  Examples:
404
436
 
@@ -406,7 +438,7 @@ Examples:
406
438
  - Missing `project_context/context.toml` can receive a conservative baseline manifest.
407
439
  - `project_context/areas/main/verification.md` can be registered as `verification` by path convention.
408
440
  - `project_context/areas/payment/api.md` without a manifest role is `manual_required`; the Harness does not guess whether it is an area, contract, foundation or implementation index.
409
- - If the target already exists, the migration is `blocked` and no file is overwritten.
441
+ - If the target already exists, the migration is `blocked`; `upgrade` stops before migrations or `sync`, and no file is overwritten.
410
442
 
411
443
  The former migration command has been removed because existing users have completed that migration path.
412
444
 
@@ -415,9 +447,11 @@ The former migration command has been removed because existing users have comple
415
447
  ```sh
416
448
  npx --yes --package project-tiny-context-harness@latest sdlc-harness init
417
449
  npx --yes --package project-tiny-context-harness@latest sdlc-harness init --adopt
418
- npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --all
450
+ npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --all
419
451
  npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --full
420
452
  npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --code
453
+ make sdlc-check-modularity
454
+ npx --yes --package project-tiny-context-harness@latest sdlc-harness check-modularity --touched
421
455
  make sdlc-sync
422
456
  make sdlc-upgrade
423
457
  npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade --check
@@ -425,11 +459,12 @@ npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade --c
425
459
  npx --yes --package project-tiny-context-harness@latest sdlc-harness validate-context
426
460
  npx --yes --package project-tiny-context-harness@latest sdlc-harness doctor
427
461
  make sdlc-doctor
428
- make validate-context
429
- make validate-harness
430
- ```
431
-
432
- `make validate-harness` is a compatibility alias for `validate-context` in vNext projects.
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.
433
468
 
434
469
  ## Current Boundary
435
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.52.tgz
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, `validate-context` or `validate-harness`. Maintainer-only package tests and source-drift checks are intentionally kept out of consumer projects.
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
 
@@ -268,9 +268,38 @@ The default Skills are Minimal Context helpers for explicit product-planning, UI
268
268
 
269
269
  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.
270
270
 
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 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
-
273
- 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.
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
+
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.
274
303
 
275
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.
276
305
 
@@ -292,14 +321,16 @@ Use `npx --no-install sdlc-harness ...` only when you explicitly want the alread
292
321
  |---|---|
293
322
  | `npx --yes --package project-tiny-context-harness@latest sdlc-harness init` | Non-destructively installs Minimal Context Harness into the current project. |
294
323
  | `make sdlc-sync` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness sync` | Refreshes managed guidance, default Skills, Makefile include, tools and templates. It does not run migrations or generate project semantics; when migration work is pending it refuses to write and tells you to run `upgrade`. |
295
- | `make sdlc-upgrade` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade` | Default command after updating the npm package. Builds an upgrade plan, applies `safe_pending` migrations, runs `sync` and `doctor`, and exits non-zero when manual or blocked follow-up remains. |
324
+ | `make sdlc-upgrade` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade` | Default command after updating the npm package. Builds an upgrade plan, stops before writes when `blocked` items exist, otherwise applies `safe_pending` migrations, runs `sync` and `doctor`, and exits non-zero when manual follow-up or diagnostics remain. |
296
325
  | `npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade --check [--json]` | Checks the upgrade plan without writing files. Reports `safe_pending`, `manual_required` and `blocked`; exits non-zero when any work remains. |
297
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/**`. |
298
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. |
299
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. |
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. |
300
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. |
301
- | `make validate-context` | Makefile wrapper for `validate-context`. |
302
- | `make validate-harness` | Compatibility alias for `validate-context` in vNext projects. |
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`. |
303
334
  | `sdlc-harness package sync-source` | Maintainer-only command to sync source workspace assets into `packages/sdlc-harness/assets/**`. |
304
335
  | `sdlc-harness package check-source` | Maintainer-only drift check for package canonical assets. |
305
336
 
@@ -327,9 +358,9 @@ Release notes and release readiness use this update mode vocabulary:
327
358
  |---|---|
328
359
  | `safe_pending` | The Harness can prove the change is inside a known Harness-owned schema, config or path convention and can apply it mechanically. |
329
360
  | `manual_required` | The file is in migration scope, but the Harness cannot prove the right semantic role or user intent. It prints the path and follow-up. |
330
- | `blocked` | A safe target cannot be written, usually because the destination already exists or another conflict would require overwriting user content. |
361
+ | `blocked` | A safe target cannot be written, usually because the destination already exists or another conflict would require overwriting user content. Blocked items stop upgrade writes until resolved. |
331
362
 
332
- `upgrade` promises to refresh package-managed assets, apply known safe migrations, avoid overwriting user custom content, expose manual-required migration scope, and run `doctor` / `validate-context` style diagnostics so remaining problems are visible. It does not automatically understand the user's project semantics, decide every Context role, repair project-local Skills, invent business verification paths, update product/deployment facts or turn an old project into the current best-practice shape.
363
+ `upgrade` promises to refresh package-managed assets, apply known safe migrations when no blocked target conflict exists, avoid overwriting user custom content, expose manual-required migration scope, and run `doctor` / `validate-context` style diagnostics so remaining problems are visible. It does not automatically understand the user's project semantics, decide every Context role, repair project-local Skills, invent business verification paths, update product/deployment facts or turn an old project into the current best-practice shape.
333
364
 
334
365
  Examples:
335
366
 
@@ -337,7 +368,7 @@ Examples:
337
368
  - A missing `project_context/context.toml` can receive a conservative baseline manifest.
338
369
  - `project_context/areas/main/verification.md` can be registered as a `verification` role by path convention.
339
370
  - `project_context/areas/payment/api.md` without a manifest role is reported as `manual_required`; the Harness does not guess whether it is an area, contract, foundation or implementation index.
340
- - If `project_context/areas/main.md` already exists while `project_context/modules/main.md` still exists, the migration is `blocked` and no file is overwritten.
371
+ - If `project_context/areas/main.md` already exists while `project_context/modules/main.md` still exists, the migration is `blocked`; `upgrade` stops before migrations or `sync`, and no file is overwritten.
341
372
 
342
373
  ## Minimal Context Files
343
374
 
@@ -13,7 +13,7 @@
13
13
  1. 先读 `project_context/global.md`、`project_context/architecture.md` 和 `project_context/context.toml`,再按 graph 读取相关 area / context unit。
14
14
  2. 若任务涉及 Web 页面、前端布局、UI/UX、产品模块边界或信息放置,先做页面产品定位检查,再完成变更分类;若 UI 改动涉及输入、选择、搜索、筛选、表单/配置、调度/时间窗口、预算/配额/限流或加载/空态/错误态,按产品/UIUX Skill 的控件任务框架做轻量检查。
15
15
  3. 若任务新增、迁移或整理 Context 文件,先做 role placement scan:area 只代表产品域归属;contract / foundation / subdomain / verification / deployment / implementation-index / decision-rationale 等按读取目的拆成 role Context。
16
- 4. 对产品方案、UI/UX、系统设计、架构边界、API / Schema、模块设计原则、状态或运行语义、验证设计等任务,先把相关模块设计上下文编译进当前任务契约;契约第一段用 `Context Delta: none|required` 声明是否改变长期事实,再写本次 Task Contract
16
+ 4. 对产品方案、UI/UX、系统设计、架构边界、API / Schema、模块设计原则、状态或运行语义、验证设计等任务,先把相关模块设计上下文编译进当前任务契约;契约第一段用 `Context Delta: none|required` 声明是否改变长期事实,再写本次 Task Contract;工程 / RFC / 实现类 Task Contract 同时包含 `Modularity Check: none|required|exception`。
17
17
  5. `Context Delta: required` 则 context-first;普通 bug fix、局部样式、局部漂移修复、测试修复或 spike 默认 code-first,但一旦产生长期结论必须回写 Context。
18
18
  6. 收尾做 Contract Conformance 和 Context drift check,只报告 `Context: 已更新 ...` 或 `Context: 本次无长期事实变化`;不要把一次性证据、任务契约或实现摘要写入 Context。
19
19
 
@@ -29,7 +29,7 @@
29
29
 
30
30
  1. 新会话或继续工作时,先读取 `project_context/global.md`、`project_context/architecture.md` 和 `project_context/context.toml`;按其中 default area 和触发条件读取相关 context。
31
31
  2. 第一处代码编辑前先做轻量变更分类,不按固定时长计时。若任务涉及 Web 页面、前端布局、UI/UX、产品模块边界或信息应该放在哪个页面 / 模块,先做页面产品定位检查,再完成变更分类:用户在这个页面要完成什么判断,产品必须提供哪些信息 / 动作 / 反馈,哪些信息不应常驻,哪些属于下游消费层 / 运维层 / 详情层 / 其他页面,当前布局和信息密度是否匹配页面任务。若 UI 改动涉及输入、选择、搜索、筛选、表单/配置、调度/时间窗口、预算/配额/限流或加载/空态/错误态,按产品/UIUX Skill 的控件任务框架做轻量检查,识别既有 Context 是否适用以及是否缺少长期页面/控件契约。多页面或多模块归属不清时,先审查全站或相关页面的信息架构,再收窄到代码模块实现。该检查是判断是否需要 context-first 的输入,不等于必须更新 Context,也不要求独立文档或新的 gate。
32
- 3. 对产品方案、UI/UX、系统设计、架构边界、API / Schema、模块设计原则、状态机或运行语义、验证设计等任务,第一处代码编辑前先编译当前任务契约:用 `Context Delta: none|required` 作为唯一正式长期事实判断点,再写本次 Task Contract,并把命中的模块设计上下文、它控制的当前选择、首选路径和 fallback / degraded path 条件写入任务契约。普通 bug fix、局部样式、小重构、局部漂移修复、测试修复或 spike 不强制编译任务契约。
32
+ 3. 对产品方案、UI/UX、系统设计、架构边界、API / Schema、模块设计原则、状态机或运行语义、验证设计等任务,第一处代码编辑前先编译当前任务契约:用 `Context Delta: none|required` 作为唯一正式长期事实判断点,再写本次 Task Contract,并把命中的模块设计上下文、它控制的当前选择、首选路径和 fallback / degraded path 条件写入任务契约;工程 / RFC / 实现类 Task Contract 还应包含 `Modularity Check: none|required|exception`。普通 bug fix、局部样式、小重构、局部漂移修复、测试修复或 spike 不强制编译任务契约。
33
33
  4. 当新增、迁移或整理 `project_context/areas/**` 时,做 role placement scan(软约束,不做 gate):`area` / `domain` 保留产品域归属,`subdomain` 用于产品域内较小 ownership,`contract` 用于 API / schema / event / 跨域接口语义,`foundation` 用于稳定理论 / 词汇 / 背景材料,`verification` / `deployment` 用于可复用执行路径,`implementation-index` 只做代码导航索引,`decision-rationale` 记录稳定设计原因,`archive` 用于非默认读取的历史或外部材料。
34
34
  5. 若任务契约声明 `Context Delta: required`,默认走 context-first:第一处代码编辑前先更新相关 `project_context/**`,写入必要且足以指导实现的长期结论,再按 Context 和 Task Contract 对齐实现、验证和收尾。
35
35
  6. 普通 bug fix、局部样式、局部实现漂移修复、测试修复或探索性 spike 不更新 Context,可先改代码;一旦形成长期结论,继续对齐或交付前必须回写 `project_context/**`。
@@ -14,14 +14,17 @@ on:
14
14
  default: "validate-context"
15
15
  type: choice
16
16
  options:
17
- - validate-context
18
- - validate-harness
19
-
20
- jobs:
21
- harness:
22
- runs-on: ubuntu-latest
23
- steps:
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-context' }}
37
- # pjsdlc:sdlc-harness:github-workflow:end
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,15 +1,18 @@
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
- .PHONY: help sdlc-doctor sdlc-sync sdlc-upgrade validate-context validate-harness lint test-current-domain test-all build
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"
9
10
  @echo " make sdlc-sync Refresh managed assets; refuses when upgrade migrations are pending"
10
11
  @echo " make sdlc-upgrade Run safe upgrade migrations, sync managed assets and doctor"
12
+ @echo " make sdlc-check-modularity Warn on oversized touched handwritten source files"
11
13
  @echo " make validate-context Check whether project_context/** supports context recovery"
12
- @echo " make validate-harness Compatibility alias for validate-context"
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"
13
16
  @echo " make test-all Run the project regression suite after replacing this placeholder"
14
17
 
15
18
  sdlc-doctor:
@@ -18,13 +21,19 @@ sdlc-doctor:
18
21
  sdlc-sync:
19
22
  $(SDLC_HARNESS) sync
20
23
 
21
- sdlc-upgrade:
22
- $(SDLC_HARNESS) upgrade
23
-
24
- validate-context:
25
- $(SDLC_HARNESS) validate-context
26
-
27
- validate-harness: validate-context
24
+ sdlc-upgrade:
25
+ $(SDLC_HARNESS) upgrade
26
+
27
+ sdlc-check-modularity:
28
+ $(SDLC_HARNESS) check-modularity $(SDLC_MODULARITY_SCOPE)
29
+
30
+ validate-context:
31
+ $(SDLC_HARNESS) validate-context
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
28
37
 
29
38
  lint:
30
39
  @echo "No project lint command configured yet. Replace this target with your stack-specific lint command."
@@ -33,6 +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` 审计计划编辑文件,用 `make validate-code-modularity` 或 `sdlc-harness check-modularity --touched --limit 300 --fail-on-warning` 做交付前硬审计。若项目本地 Skill 定义了不同 limit,使用项目本地值。
37
+ - 发现超限 touched file 后,不只记录行数;判断本次是否在该文件加入新职责,并回到本节拆分原则选择产品面、hook、model、adapter、component、service / facade 或 verification helper 等边界。避免只按行数机械拆分、但耦合和职责仍留在原处。
38
+ - 如果本次不拆,`Modularity Check` 取 `exception`,必须有 `<harnessRoot>/config.yaml` 的 `modularity.waivers` 记录(`path`、收窄 `category`、`reason`、`future_split_boundary`);交付说明只能补充说明,不是机器豁免。若项目设置 `modularity.policy: strict_except_generated`,legacy waiver 不可用,超限手写源码必须拆分或停止触碰。已豁免历史债也不得继续塞新职责,除非本次任务就是拆分 / 迁移。
36
39
  - 查找相似实现、重复逻辑、紧耦合模块或影响面异常扩散点。
37
40
  - 当一个业务对象、能力或接口的变更需要跨多个 Context、产品域或实现层同步调整时,将该影响范围视为模块边界复核信号;优先评估是否应通过独立模块、服务、facade 或稳定接口收敛依赖,避免通过手工 manifest 长期复制实现暴露面。
38
41
  - 将候选项分为局部重构与长期边界变化,后者按既有 Context-first 规则处理。
@@ -63,6 +66,10 @@ Project-specific engineering rules belong in a separate project-local Skill unde
63
66
  - `none`:本次只是按既有 Context / 架构原则落地,不新增长期事实。
64
67
  - `required`:说明长期事实类型、应写入的 Context / role、需要沉淀的事实,以及明确不写入 Context 的一次性内容。
65
68
  - `Task Contract` 用短列表说明 capability、owner、upstream / downstream、allowed / forbidden dependency、input / output / state / persistence、failure / retry / timeout / degraded / recovery、observability、performance、security、non-goals 和 verification path。
69
+ - 工程 / RFC / 实现类任务的 `Task Contract` 必须包含 `Modularity Check: none|required|exception`:
70
+ - `none`:没有超限计划 / touched 手写源码文件,或本次没有向超限文件增加新职责。
71
+ - `required`:拆分是本次验收条件,应按 abstraction / decomposition scan 的职责边界完成。
72
+ - `exception`:本次触碰超限文件但暂不拆;只有默认 `modularity.policy: scoped_waivers` 允许此路径,且必须已有或同步新增 `<harnessRoot>/config.yaml` `modularity.waivers` 记录文件、收窄分类、原因和后续拆分边界。若项目设置 `modularity.policy: strict_except_generated`,不得用 legacy waiver 绕过超限手写源码,交付说明只记录本次是否新增职责以及为什么没有拆。
66
73
  - `Applicable Module Design` 是高风险任务的前置字段:列出命中的 Context / Skill 来源、适用的 Principles、Design Logic 和 Design Rationale,以及它们控制的当前实现或验证选择。
67
74
  - `Principle Decision Gate` 要写明首选执行路径、fallback / degraded path 的进入条件,以及什么证据不能证明本次目标。涉及 capability、metric 或 acceptance claim 时,先声明要证明的 claim,再选择命令或 probe。
68
75
  - 对长任务、多模块、多 agent、容易发生 `Context Delta` 调头或多轮验证的任务,可以用 `plan.md` 或等价临时计划面暂存 `Context Delta`、`Task Contract`、`Implementation Steps` 和 `Contract Conformance`;它只是临时执行缓存。
@@ -35,10 +35,11 @@ When the user asks to upgrade Tiny Context / Project Tiny Context Harness in an
35
35
  6. Do not run standalone `sync` after a successful `upgrade` unless release notes say the update is `sync-only`, the project wrapper did not run sync, or the user explicitly asks for a managed-asset refresh.
36
36
  7. If `upgrade --check` or `upgrade` reports only `safe_pending` items and the command succeeds, do not invent additional manual cleanup.
37
37
  8. If the report includes `manual_required` or `blocked`, handle only the listed migration scope. Use `project_context/context.toml`, role placement scan and the existing area graph to decide placement. Do not guess product or business semantics.
38
- 9. Run diagnostics after migration-scoped follow-up:
38
+ 9. If the report includes `blocked`, treat it as a write preflight failure: resolve the blocked migration scope and rerun `upgrade` before expecting safe migrations or managed asset sync to have been applied.
39
+ 10. Run diagnostics after migration-scoped follow-up:
39
40
  - `make sdlc-doctor` or the CLI `doctor`
40
41
  - `make validate-context`
41
- 10. Report commands run, migration status, diagnostics, files changed and any remaining manual items. Use `Context: no durable project facts changed` unless the upgrade exposed or required a real long-term project fact change.
42
+ 11. Report commands run, migration status, diagnostics, files changed and any remaining manual items. Use `Context: no durable project facts changed` unless the upgrade exposed or required a real long-term project fact change.
42
43
 
43
44
  ## Manual Handling Rules
44
45
 
@@ -0,0 +1 @@
1
+ export declare function checkModularity(args: string[]): Promise<void>;
@@ -0,0 +1,145 @@
1
+ import { runModularityCheck } from "../lib/modularity.js";
2
+ export async function checkModularity(args) {
3
+ let parsed;
4
+ try {
5
+ parsed = parseArgs(args);
6
+ }
7
+ catch (error) {
8
+ console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
9
+ process.exitCode = 1;
10
+ return;
11
+ }
12
+ if (parsed.help || (!parsed.touched && !parsed.base && parsed.files.length === 0)) {
13
+ console.log(helpText());
14
+ if (!parsed.help) {
15
+ process.exitCode = 1;
16
+ }
17
+ return;
18
+ }
19
+ try {
20
+ const report = await runModularityCheck(process.cwd(), {
21
+ touched: parsed.touched,
22
+ base: parsed.base,
23
+ files: parsed.files,
24
+ limit: parsed.limit
25
+ });
26
+ console.log(`check-modularity audited=${report.files.length} warning=${report.warnings.length} limit=${report.limit} waived=${report.waivedWarnings.length}`);
27
+ if (report.files.length === 0) {
28
+ console.log("No handwritten source files matched the selected scope.");
29
+ }
30
+ for (const file of report.files) {
31
+ const prefix = file.overLimit && file.waived ? "waived" : file.overLimit ? "over-limit" : "ok";
32
+ console.log(`${prefix}: ${file.relativePath} ${file.lines} lines`);
33
+ }
34
+ for (const error of report.errors) {
35
+ console.error(`error: ${error}`);
36
+ }
37
+ for (const warning of report.warnings) {
38
+ console.warn(`warning: ${warning}`);
39
+ }
40
+ for (const waiver of report.waivedWarnings) {
41
+ console.warn(`waived: ${waiver}`);
42
+ }
43
+ if (report.warnings.length > 0) {
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.");
45
+ }
46
+ if (report.errors.length > 0 || (report.warnings.length > 0 && parsed.failOnWarning)) {
47
+ process.exitCode = 1;
48
+ }
49
+ }
50
+ catch (error) {
51
+ console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
52
+ process.exitCode = 1;
53
+ }
54
+ }
55
+ function parseArgs(args) {
56
+ const parsed = {
57
+ touched: false,
58
+ files: [],
59
+ limit: 300,
60
+ failOnWarning: false,
61
+ help: false
62
+ };
63
+ for (let index = 0; index < args.length; index += 1) {
64
+ const arg = args[index];
65
+ if (arg === "--touched") {
66
+ parsed.touched = true;
67
+ continue;
68
+ }
69
+ if (arg === "--fail-on-warning") {
70
+ parsed.failOnWarning = true;
71
+ continue;
72
+ }
73
+ if (arg === "--help" || arg === "-h") {
74
+ parsed.help = true;
75
+ continue;
76
+ }
77
+ if (arg === "--file") {
78
+ const value = args[index + 1];
79
+ if (!value || value.startsWith("--")) {
80
+ throw new Error("check-modularity --file requires a path");
81
+ }
82
+ parsed.files.push(value);
83
+ index += 1;
84
+ continue;
85
+ }
86
+ if (arg.startsWith("--file=")) {
87
+ const value = arg.slice("--file=".length).trim();
88
+ if (!value) {
89
+ throw new Error("check-modularity --file requires a path");
90
+ }
91
+ parsed.files.push(value);
92
+ continue;
93
+ }
94
+ if (arg === "--base") {
95
+ const value = args[index + 1];
96
+ if (!value || value.startsWith("--")) {
97
+ throw new Error("check-modularity --base requires a ref");
98
+ }
99
+ parsed.base = value;
100
+ index += 1;
101
+ continue;
102
+ }
103
+ if (arg.startsWith("--base=")) {
104
+ const value = arg.slice("--base=".length).trim();
105
+ if (!value) {
106
+ throw new Error("check-modularity --base requires a ref");
107
+ }
108
+ parsed.base = value;
109
+ continue;
110
+ }
111
+ if (arg === "--limit") {
112
+ const value = args[index + 1];
113
+ if (!value || value.startsWith("--")) {
114
+ throw new Error("check-modularity --limit requires a positive integer");
115
+ }
116
+ parsed.limit = parseLimit(value);
117
+ index += 1;
118
+ continue;
119
+ }
120
+ if (arg.startsWith("--limit=")) {
121
+ parsed.limit = parseLimit(arg.slice("--limit=".length));
122
+ continue;
123
+ }
124
+ throw new Error(`unknown check-modularity argument: ${arg}`);
125
+ }
126
+ return parsed;
127
+ }
128
+ function parseLimit(value) {
129
+ const limit = Number.parseInt(value, 10);
130
+ if (!Number.isInteger(limit) || limit <= 0 || String(limit) !== value.trim()) {
131
+ throw new Error("check-modularity --limit requires a positive integer");
132
+ }
133
+ return limit;
134
+ }
135
+ function helpText() {
136
+ return `sdlc-harness check-modularity:
137
+ check-modularity --touched [--limit 300] [--fail-on-warning]
138
+ check-modularity --file <path> [--file <path> ...] [--limit 300] [--fail-on-warning]
139
+ check-modularity --base <ref> [--limit 300] [--fail-on-warning]
140
+
141
+ Audits selected handwritten source files for physical line-count risk.
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.`;
145
+ }
@@ -1,3 +1,4 @@
1
+ import { checkModularity } from "./check-modularity.js";
1
2
  import { doctor } from "./doctor.js";
2
3
  import { exportContext } from "./export-context.js";
3
4
  import { init } from "./init.js";
@@ -11,9 +12,11 @@ export const commands = {
11
12
  sync,
12
13
  upgrade,
13
14
  doctor,
15
+ "check-modularity": checkModularity,
14
16
  "export-context": exportContext,
15
17
  validate,
16
18
  "validate-context": (args) => validate(["validate-context", ...args]),
19
+ "validate-code-modularity": (args) => validate(["validate-code-modularity", ...args]),
17
20
  "validate-harness": (args) => validate(["validate-harness", ...args]),
18
21
  package: packageSource
19
22
  };
@@ -24,11 +27,15 @@ export function help() {
24
27
  sync Refresh managed assets; refuses when upgrade migrations are pending
25
28
  upgrade [--check] [--json]
26
29
  Run safe migrations, sync managed assets and doctor
27
- doctor Diagnose project configuration and drift
28
- export-context --full|--code|--all [--output <path>] [--check]
29
- Export a temporary Context summary or code implementation Markdown artifact
30
- validate <gate> Run a Harness validation gate (Minimal Context only)
31
- validate-context Validate Minimal Context fact-source recoverability
32
- validate-harness Compatibility alias for validate-context
30
+ doctor Diagnose project configuration and drift
31
+ check-modularity --touched|--file <path>|--base <ref> [--limit 300] [--fail-on-warning]
32
+ Warn when selected handwritten source files exceed a line-count limit
33
+ export-context --full|--code|--all [--output <path>] [--check]
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
33
40
  package <subcommand> Maintain package canonical source`);
34
41
  }
@@ -1,5 +1,5 @@
1
1
  import { createUpgradePlan, formatUpgradePlan, hasUpgradePlanWork, updateModeForPlan } from "../lib/migrations.js";
2
- import { runUpgrade } from "../lib/upgrade.js";
2
+ import { runUpgradeReport } from "../lib/upgrade.js";
3
3
  export async function upgrade(args = []) {
4
4
  const options = parseArgs(args);
5
5
  if (options.help) {
@@ -21,14 +21,20 @@ export async function upgrade(args = []) {
21
21
  }
22
22
  return;
23
23
  }
24
- const report = await runUpgrade(process.cwd());
24
+ const report = await runUpgradeReport(process.cwd());
25
25
  if (options.json) {
26
- console.log(JSON.stringify({ lines: report }, null, 2));
26
+ console.log(JSON.stringify(report, null, 2));
27
+ if (report.blocked) {
28
+ process.exitCode = 1;
29
+ }
27
30
  return;
28
31
  }
29
- for (const line of report) {
32
+ for (const line of report.lines) {
30
33
  console.log(line);
31
34
  }
35
+ if (report.blocked) {
36
+ process.exitCode = 1;
37
+ }
32
38
  }
33
39
  function parseArgs(args) {
34
40
  const options = { check: false, json: false, help: false };