@drafthq/draft 3.4.0 → 3.5.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/core/templates/okf/concept.md +9 -0
- package/integrations/agents/AGENTS.md +86 -20
- package/integrations/copilot/.github/copilot-instructions.md +86 -20
- package/package.json +1 -1
- package/scripts/lib.sh +5 -0
- package/scripts/tools/okf-coverage-check.sh +192 -0
- package/scripts/tools/okf-plan-concepts.sh +296 -0
- package/scripts/tools/okf-render-views.sh +30 -0
- package/scripts/tools/okf-validate-all.sh +117 -0
- package/scripts/tools/okf-validate-quality.sh +272 -0
- package/scripts/tools/okf-validate.sh +35 -1
- package/skills/init/SKILL.md +10 -0
- package/skills/init/references/okf-emitter.md +67 -20
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "draft",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
|
|
15
|
-
"version": "3.
|
|
15
|
+
"version": "3.5.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "mayurpise"
|
|
18
18
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "draft",
|
|
3
3
|
"displayName": "Draft",
|
|
4
4
|
"description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.5.0",
|
|
6
6
|
"skills": "./skills/",
|
|
7
7
|
"agents": "./core/agents/",
|
|
8
8
|
"author": {
|
|
@@ -52,3 +52,12 @@ so a focused task knows exactly which source files to open.
|
|
|
52
52
|
## See also
|
|
53
53
|
|
|
54
54
|
- [Related concept](../systems/other.md)
|
|
55
|
+
|
|
56
|
+
<!-- okf:concept-template v1
|
|
57
|
+
A stub or redirect-only page (e.g. "see architecture.md", an unreplaced
|
|
58
|
+
{PLACEHOLDER} token, or a body below the per-type minimum) FAILS
|
|
59
|
+
okf-validate-quality.sh and therefore does NOT satisfy okf-coverage-check.sh —
|
|
60
|
+
the bundle will not be promoted. Diagram types (Subsystem/Module/Feature/
|
|
61
|
+
Entrypoint) require ≥1 valid Mermaid block and ≥2 x-grounded-paths.
|
|
62
|
+
-->
|
|
63
|
+
|
|
@@ -336,6 +336,16 @@ DRAFT_INIT_MODE="${DRAFT_INIT_MODE:-auto}"
|
|
|
336
336
|
|
|
337
337
|
The tier-gated default rests on **maintainability/readability** (one navigable concept per file, cleaner PRs, a generated `architecture.md` preserved for linear onboarding) — not on the A/B benchmark, which was accuracy-parity (`docs/audit/okf-benchmark.md`). `monolith` is **retained, not retired**: it is the tier-1/2 default, the A/B baseline, and the fallback. If `DRAFT_INIT_MODE` is unset, do not commit to a mode until **Step 1.4.5** has computed the tier.
|
|
338
338
|
|
|
339
|
+
**OKF Completeness Verification (blocking — tier 3+ `okf` mode).** Completeness is enforced by tooling, not honor system, so the wiki is generated completely for every module/sub-module/component on every run. Before promoting `draft.tmp/` → `draft/`, ALL must hold (see `references/okf-emitter.md` for the pipeline):
|
|
340
|
+
|
|
341
|
+
1. `okf-plan-concepts.sh` ran and the expected/required/deferred counts were logged **before** any page was written (the concept boundary is a tool output, not an in-context guess).
|
|
342
|
+
2. Every `required` entry in `concept-plan.json` has a non-stub page.
|
|
343
|
+
3. `okf-validate-all.sh … --plan … --strict` exits 0 (structure + per-type quality + coverage all pass).
|
|
344
|
+
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) — no required package with `fan_in ≥ floor` is **MISSING**.
|
|
345
|
+
5. On any failure: **do not** atomic-rename; surface `.state/validation-report.json`.
|
|
346
|
+
|
|
347
|
+
> **Red flag:** writing concept pages without first running `okf-plan-concepts.sh`, or finishing generation while any `required` plan entry is unwritten, is a **completeness failure** — not a stylistic one.
|
|
348
|
+
|
|
339
349
|
### Route Explicit Modes Before Initialization
|
|
340
350
|
|
|
341
351
|
If the user explicitly invoked a specialist mode, route directly:
|
|
@@ -3291,7 +3301,10 @@ esac
|
|
|
3291
3301
|
Everything else in `draft init` (5-phase analysis, graph snapshot, `.state/`
|
|
3292
3302
|
hashing, scope detection, atomic staging) is **reused unchanged**. This mode adds
|
|
3293
3303
|
a decomposition + serialization stage. It introduces **no new LLM analysis
|
|
3294
|
-
engine
|
|
3304
|
+
engine**; its deterministic helpers are `okf-plan-concepts.sh` (expected-concept
|
|
3305
|
+
set), `okf-validate.sh` (structure), `okf-validate-quality.sh` (per-type
|
|
3306
|
+
anti-stub), `okf-coverage-check.sh` (completeness), and `okf-validate-all.sh`
|
|
3307
|
+
(the single promotion gate that runs all three), plus `okf-render-views.sh`.
|
|
3295
3308
|
|
|
3296
3309
|
## Target layout
|
|
3297
3310
|
|
|
@@ -3384,25 +3397,64 @@ Derive concepts from the graph, not by hand:
|
|
|
3384
3397
|
|
|
3385
3398
|
```
|
|
3386
3399
|
1. Survey → existing draft init 5-phase + graph snapshot (graph-snapshot.sh)
|
|
3387
|
-
2. Plan →
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3400
|
+
2. Plan → DETERMINISTIC. okf-plan-concepts.sh derives the expected-concept
|
|
3401
|
+
set from the graph (every package with fan_in ≥ floor → required
|
|
3402
|
+
Subsystem/Module; entrypoints → required; below-floor / allow-defer
|
|
3403
|
+
→ deferred with a reason) and writes draft.tmp/.state/concept-plan.json.
|
|
3404
|
+
okf-plan-concepts.sh --repo . [--scope PATH] \
|
|
3405
|
+
[--manifest FILE] [--min-fan-in 2] [--allow-defer GLOB]... \
|
|
3406
|
+
--out draft.tmp/.state/concept-plan.json
|
|
3407
|
+
This replaces the old in-context concept enumeration — the boundary
|
|
3408
|
+
of the work is now a tool output, not an LLM judgment, so modules
|
|
3409
|
+
cannot be silently dropped. LOG the counts (expected/required/
|
|
3410
|
+
deferred) BEFORE writing any page. `generated_order` is topo-ish
|
|
3411
|
+
(required + high-fan-in first) so forward cross-links resolve.
|
|
3412
|
+
3. Generate → iterate concept-plan.generated_order; write ONE page per REQUIRED
|
|
3413
|
+
entry, grounding each from the graph:
|
|
3392
3414
|
x-callers ← graph-callers.sh --symbol <c>
|
|
3393
3415
|
x-grounded-paths ← graph-impact.sh --symbol <c> (blast radius)
|
|
3394
3416
|
x-hotspot-score ← hotspot-rank.sh
|
|
3395
3417
|
overview diagrams ← mermaid-from-graph.sh
|
|
3396
3418
|
Record each source path → page in .state/path-to-concept.json.
|
|
3419
|
+
Loop post-condition: every required concept_id has an output file.
|
|
3420
|
+
⚠ Writing pages via shell heredoc without reading x-grounded-paths
|
|
3421
|
+
sources, or finishing while any required entry is unwritten, is a
|
|
3422
|
+
completeness failure — not a stylistic one.
|
|
3397
3423
|
4. Render views → ai-context.md (synopsis + Concept Map), architecture.md
|
|
3398
|
-
(concatenated view), wiki/log.md (see M4).
|
|
3399
|
-
5. Validate →
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3424
|
+
(concatenated view + coverage banner), wiki/log.md (see M4).
|
|
3425
|
+
5. Validate → the promotion gate. Run all layers via the orchestrator:
|
|
3426
|
+
5a. okf-validate-all.sh draft.tmp/wiki \
|
|
3427
|
+
--repo . \
|
|
3428
|
+
--plan draft.tmp/.state/concept-plan.json \
|
|
3429
|
+
--path-index draft.tmp/.state/path-to-concept.json \
|
|
3430
|
+
--strict --report draft.tmp/.state/validation-report.json
|
|
3431
|
+
It runs, in order: okf-validate.sh (structure + reverse index),
|
|
3432
|
+
okf-validate-quality.sh (per-type anti-stub / depth / mermaid lint),
|
|
3433
|
+
okf-coverage-check.sh (every required plan entry → real page).
|
|
3434
|
+
ANY layer failing ⇒ exit non-zero ⇒ DO NOT atomic-rename.
|
|
3435
|
+
coverage.md (systems/coverage.md) is regenerated by the coverage
|
|
3436
|
+
layer; it is tool-owned (marker <!-- okf:coverage-generated -->) —
|
|
3437
|
+
never hand-author it except deferral reasons in the manifest.
|
|
3438
|
+
6. Emit → mv draft.tmp/ draft/ ONLY IF step 5 exit 0 ; update .state/.
|
|
3439
|
+
On failure keep draft.tmp/ and surface validation-report.json.
|
|
3440
|
+
```
|
|
3441
|
+
|
|
3442
|
+
### Validation report schema (`.state/validation-report.json`)
|
|
3443
|
+
|
|
3444
|
+
```json
|
|
3445
|
+
{ "valid": false, "bundle": "draft.tmp/wiki",
|
|
3446
|
+
"layers": { "structure": "pass", "quality": "pass", "coverage": "fail" } }
|
|
3404
3447
|
```
|
|
3405
3448
|
|
|
3449
|
+
### Component manifest (optional — `--manifest FILE`)
|
|
3450
|
+
|
|
3451
|
+
When the graph engine is unavailable (or a repo wants an authoritative list), pass
|
|
3452
|
+
a plain-text manifest: one component name per line, `#` comments and blanks ignored.
|
|
3453
|
+
Every listed component becomes a REQUIRED concept; `--allow-defer GLOB` still moves
|
|
3454
|
+
matches to deferred. Without a manifest the plan comes from the graph, and only if
|
|
3455
|
+
both are unavailable does it fall back to a heuristic top-level-dir scan (which it
|
|
3456
|
+
marks `degraded: true`).
|
|
3457
|
+
|
|
3406
3458
|
Page bodies are LLM-narrated for readability **but** the graph-derived
|
|
3407
3459
|
frontmatter and the `Blast radius`/`Used by` sections are deterministic. To keep
|
|
3408
3460
|
incremental carry-forward byte-identical (open decision 2), cache the narrated
|
|
@@ -3456,17 +3508,22 @@ section `index.md` tables are the injection slots for the routing tables.
|
|
|
3456
3508
|
`draft init refresh` under `okf` mode:
|
|
3457
3509
|
|
|
3458
3510
|
```
|
|
3459
|
-
1.
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
6.
|
|
3511
|
+
1. Re-derive the plan: okf-plan-concepts.sh (modules added since last run become
|
|
3512
|
+
REQUIRED — a new package can't slip through a refresh either)
|
|
3513
|
+
2. Diff hashes.json vs working tree → changed source paths
|
|
3514
|
+
3. path-to-concept.json → affected concept pages
|
|
3515
|
+
4. Regenerate ONLY affected concepts; carry the rest verbatim (cached narration)
|
|
3516
|
+
5. Re-render ai-context.md / architecture.md / log.md (cheap; always regenerated)
|
|
3517
|
+
6. Re-validate (full gate): okf-validate-all.sh on the bundle with --plan and
|
|
3518
|
+
--path-index. Refresh re-runs structure + quality + coverage — a changed
|
|
3519
|
+
concept must still clear the quality bar, and a newly-required module must
|
|
3520
|
+
still be present.
|
|
3521
|
+
7. Append log.md; update hashes.json + path-to-concept.json
|
|
3466
3522
|
```
|
|
3467
3523
|
|
|
3468
3524
|
A 1-file change regenerates only the concept(s) that file grounds. Unchanged
|
|
3469
|
-
concepts are byte-identical across runs.
|
|
3525
|
+
concepts are byte-identical across runs. The full gate still runs, so refresh
|
|
3526
|
+
cannot promote a bundle that a newly-added module left incomplete.
|
|
3470
3527
|
|
|
3471
3528
|
## Backward compatibility (§9)
|
|
3472
3529
|
|
|
@@ -22687,6 +22744,15 @@ so a focused task knows exactly which source files to open.
|
|
|
22687
22744
|
|
|
22688
22745
|
- [Related concept](../systems/other.md)
|
|
22689
22746
|
|
|
22747
|
+
<!-- okf:concept-template v1
|
|
22748
|
+
A stub or redirect-only page (e.g. "see architecture.md", an unreplaced
|
|
22749
|
+
{PLACEHOLDER} token, or a body below the per-type minimum) FAILS
|
|
22750
|
+
okf-validate-quality.sh and therefore does NOT satisfy okf-coverage-check.sh —
|
|
22751
|
+
the bundle will not be promoted. Diagram types (Subsystem/Module/Feature/
|
|
22752
|
+
Entrypoint) require ≥1 valid Mermaid block and ≥2 x-grounded-paths.
|
|
22753
|
+
-->
|
|
22754
|
+
|
|
22755
|
+
|
|
22690
22756
|
</core-file>
|
|
22691
22757
|
|
|
22692
22758
|
---
|
|
@@ -336,6 +336,16 @@ DRAFT_INIT_MODE="${DRAFT_INIT_MODE:-auto}"
|
|
|
336
336
|
|
|
337
337
|
The tier-gated default rests on **maintainability/readability** (one navigable concept per file, cleaner PRs, a generated `architecture.md` preserved for linear onboarding) — not on the A/B benchmark, which was accuracy-parity (`docs/audit/okf-benchmark.md`). `monolith` is **retained, not retired**: it is the tier-1/2 default, the A/B baseline, and the fallback. If `DRAFT_INIT_MODE` is unset, do not commit to a mode until **Step 1.4.5** has computed the tier.
|
|
338
338
|
|
|
339
|
+
**OKF Completeness Verification (blocking — tier 3+ `okf` mode).** Completeness is enforced by tooling, not honor system, so the wiki is generated completely for every module/sub-module/component on every run. Before promoting `draft.tmp/` → `draft/`, ALL must hold (see `references/okf-emitter.md` for the pipeline):
|
|
340
|
+
|
|
341
|
+
1. `okf-plan-concepts.sh` ran and the expected/required/deferred counts were logged **before** any page was written (the concept boundary is a tool output, not an in-context guess).
|
|
342
|
+
2. Every `required` entry in `concept-plan.json` has a non-stub page.
|
|
343
|
+
3. `okf-validate-all.sh … --plan … --strict` exits 0 (structure + per-type quality + coverage all pass).
|
|
344
|
+
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) — no required package with `fan_in ≥ floor` is **MISSING**.
|
|
345
|
+
5. On any failure: **do not** atomic-rename; surface `.state/validation-report.json`.
|
|
346
|
+
|
|
347
|
+
> **Red flag:** writing concept pages without first running `okf-plan-concepts.sh`, or finishing generation while any `required` plan entry is unwritten, is a **completeness failure** — not a stylistic one.
|
|
348
|
+
|
|
339
349
|
### Route Explicit Modes Before Initialization
|
|
340
350
|
|
|
341
351
|
If the user explicitly invoked a specialist mode, route directly:
|
|
@@ -3291,7 +3301,10 @@ esac
|
|
|
3291
3301
|
Everything else in `draft init` (5-phase analysis, graph snapshot, `.state/`
|
|
3292
3302
|
hashing, scope detection, atomic staging) is **reused unchanged**. This mode adds
|
|
3293
3303
|
a decomposition + serialization stage. It introduces **no new LLM analysis
|
|
3294
|
-
engine
|
|
3304
|
+
engine**; its deterministic helpers are `okf-plan-concepts.sh` (expected-concept
|
|
3305
|
+
set), `okf-validate.sh` (structure), `okf-validate-quality.sh` (per-type
|
|
3306
|
+
anti-stub), `okf-coverage-check.sh` (completeness), and `okf-validate-all.sh`
|
|
3307
|
+
(the single promotion gate that runs all three), plus `okf-render-views.sh`.
|
|
3295
3308
|
|
|
3296
3309
|
## Target layout
|
|
3297
3310
|
|
|
@@ -3384,25 +3397,64 @@ Derive concepts from the graph, not by hand:
|
|
|
3384
3397
|
|
|
3385
3398
|
```
|
|
3386
3399
|
1. Survey → existing draft init 5-phase + graph snapshot (graph-snapshot.sh)
|
|
3387
|
-
2. Plan →
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3400
|
+
2. Plan → DETERMINISTIC. okf-plan-concepts.sh derives the expected-concept
|
|
3401
|
+
set from the graph (every package with fan_in ≥ floor → required
|
|
3402
|
+
Subsystem/Module; entrypoints → required; below-floor / allow-defer
|
|
3403
|
+
→ deferred with a reason) and writes draft.tmp/.state/concept-plan.json.
|
|
3404
|
+
okf-plan-concepts.sh --repo . [--scope PATH] \
|
|
3405
|
+
[--manifest FILE] [--min-fan-in 2] [--allow-defer GLOB]... \
|
|
3406
|
+
--out draft.tmp/.state/concept-plan.json
|
|
3407
|
+
This replaces the old in-context concept enumeration — the boundary
|
|
3408
|
+
of the work is now a tool output, not an LLM judgment, so modules
|
|
3409
|
+
cannot be silently dropped. LOG the counts (expected/required/
|
|
3410
|
+
deferred) BEFORE writing any page. `generated_order` is topo-ish
|
|
3411
|
+
(required + high-fan-in first) so forward cross-links resolve.
|
|
3412
|
+
3. Generate → iterate concept-plan.generated_order; write ONE page per REQUIRED
|
|
3413
|
+
entry, grounding each from the graph:
|
|
3392
3414
|
x-callers ← graph-callers.sh --symbol <c>
|
|
3393
3415
|
x-grounded-paths ← graph-impact.sh --symbol <c> (blast radius)
|
|
3394
3416
|
x-hotspot-score ← hotspot-rank.sh
|
|
3395
3417
|
overview diagrams ← mermaid-from-graph.sh
|
|
3396
3418
|
Record each source path → page in .state/path-to-concept.json.
|
|
3419
|
+
Loop post-condition: every required concept_id has an output file.
|
|
3420
|
+
⚠ Writing pages via shell heredoc without reading x-grounded-paths
|
|
3421
|
+
sources, or finishing while any required entry is unwritten, is a
|
|
3422
|
+
completeness failure — not a stylistic one.
|
|
3397
3423
|
4. Render views → ai-context.md (synopsis + Concept Map), architecture.md
|
|
3398
|
-
(concatenated view), wiki/log.md (see M4).
|
|
3399
|
-
5. Validate →
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3424
|
+
(concatenated view + coverage banner), wiki/log.md (see M4).
|
|
3425
|
+
5. Validate → the promotion gate. Run all layers via the orchestrator:
|
|
3426
|
+
5a. okf-validate-all.sh draft.tmp/wiki \
|
|
3427
|
+
--repo . \
|
|
3428
|
+
--plan draft.tmp/.state/concept-plan.json \
|
|
3429
|
+
--path-index draft.tmp/.state/path-to-concept.json \
|
|
3430
|
+
--strict --report draft.tmp/.state/validation-report.json
|
|
3431
|
+
It runs, in order: okf-validate.sh (structure + reverse index),
|
|
3432
|
+
okf-validate-quality.sh (per-type anti-stub / depth / mermaid lint),
|
|
3433
|
+
okf-coverage-check.sh (every required plan entry → real page).
|
|
3434
|
+
ANY layer failing ⇒ exit non-zero ⇒ DO NOT atomic-rename.
|
|
3435
|
+
coverage.md (systems/coverage.md) is regenerated by the coverage
|
|
3436
|
+
layer; it is tool-owned (marker <!-- okf:coverage-generated -->) —
|
|
3437
|
+
never hand-author it except deferral reasons in the manifest.
|
|
3438
|
+
6. Emit → mv draft.tmp/ draft/ ONLY IF step 5 exit 0 ; update .state/.
|
|
3439
|
+
On failure keep draft.tmp/ and surface validation-report.json.
|
|
3440
|
+
```
|
|
3441
|
+
|
|
3442
|
+
### Validation report schema (`.state/validation-report.json`)
|
|
3443
|
+
|
|
3444
|
+
```json
|
|
3445
|
+
{ "valid": false, "bundle": "draft.tmp/wiki",
|
|
3446
|
+
"layers": { "structure": "pass", "quality": "pass", "coverage": "fail" } }
|
|
3404
3447
|
```
|
|
3405
3448
|
|
|
3449
|
+
### Component manifest (optional — `--manifest FILE`)
|
|
3450
|
+
|
|
3451
|
+
When the graph engine is unavailable (or a repo wants an authoritative list), pass
|
|
3452
|
+
a plain-text manifest: one component name per line, `#` comments and blanks ignored.
|
|
3453
|
+
Every listed component becomes a REQUIRED concept; `--allow-defer GLOB` still moves
|
|
3454
|
+
matches to deferred. Without a manifest the plan comes from the graph, and only if
|
|
3455
|
+
both are unavailable does it fall back to a heuristic top-level-dir scan (which it
|
|
3456
|
+
marks `degraded: true`).
|
|
3457
|
+
|
|
3406
3458
|
Page bodies are LLM-narrated for readability **but** the graph-derived
|
|
3407
3459
|
frontmatter and the `Blast radius`/`Used by` sections are deterministic. To keep
|
|
3408
3460
|
incremental carry-forward byte-identical (open decision 2), cache the narrated
|
|
@@ -3456,17 +3508,22 @@ section `index.md` tables are the injection slots for the routing tables.
|
|
|
3456
3508
|
`draft init refresh` under `okf` mode:
|
|
3457
3509
|
|
|
3458
3510
|
```
|
|
3459
|
-
1.
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
6.
|
|
3511
|
+
1. Re-derive the plan: okf-plan-concepts.sh (modules added since last run become
|
|
3512
|
+
REQUIRED — a new package can't slip through a refresh either)
|
|
3513
|
+
2. Diff hashes.json vs working tree → changed source paths
|
|
3514
|
+
3. path-to-concept.json → affected concept pages
|
|
3515
|
+
4. Regenerate ONLY affected concepts; carry the rest verbatim (cached narration)
|
|
3516
|
+
5. Re-render ai-context.md / architecture.md / log.md (cheap; always regenerated)
|
|
3517
|
+
6. Re-validate (full gate): okf-validate-all.sh on the bundle with --plan and
|
|
3518
|
+
--path-index. Refresh re-runs structure + quality + coverage — a changed
|
|
3519
|
+
concept must still clear the quality bar, and a newly-required module must
|
|
3520
|
+
still be present.
|
|
3521
|
+
7. Append log.md; update hashes.json + path-to-concept.json
|
|
3466
3522
|
```
|
|
3467
3523
|
|
|
3468
3524
|
A 1-file change regenerates only the concept(s) that file grounds. Unchanged
|
|
3469
|
-
concepts are byte-identical across runs.
|
|
3525
|
+
concepts are byte-identical across runs. The full gate still runs, so refresh
|
|
3526
|
+
cannot promote a bundle that a newly-added module left incomplete.
|
|
3470
3527
|
|
|
3471
3528
|
## Backward compatibility (§9)
|
|
3472
3529
|
|
|
@@ -22687,6 +22744,15 @@ so a focused task knows exactly which source files to open.
|
|
|
22687
22744
|
|
|
22688
22745
|
- [Related concept](../systems/other.md)
|
|
22689
22746
|
|
|
22747
|
+
<!-- okf:concept-template v1
|
|
22748
|
+
A stub or redirect-only page (e.g. "see architecture.md", an unreplaced
|
|
22749
|
+
{PLACEHOLDER} token, or a body below the per-type minimum) FAILS
|
|
22750
|
+
okf-validate-quality.sh and therefore does NOT satisfy okf-coverage-check.sh —
|
|
22751
|
+
the bundle will not be promoted. Diagram types (Subsystem/Module/Feature/
|
|
22752
|
+
Entrypoint) require ≥1 valid Mermaid block and ≥2 x-grounded-paths.
|
|
22753
|
+
-->
|
|
22754
|
+
|
|
22755
|
+
|
|
22690
22756
|
</core-file>
|
|
22691
22757
|
|
|
22692
22758
|
---
|
package/package.json
CHANGED
package/scripts/lib.sh
CHANGED
|
@@ -197,6 +197,11 @@ TOOLS=(
|
|
|
197
197
|
# OKF taxonomy emitter (DRAFT_INIT_MODE=okf)
|
|
198
198
|
"okf-validate.sh"
|
|
199
199
|
"okf-render-views.sh"
|
|
200
|
+
# OKF completeness enforcement (deterministic plan + coverage/quality gates)
|
|
201
|
+
"okf-plan-concepts.sh"
|
|
202
|
+
"okf-validate-quality.sh"
|
|
203
|
+
"okf-coverage-check.sh"
|
|
204
|
+
"okf-validate-all.sh"
|
|
200
205
|
)
|
|
201
206
|
|
|
202
207
|
# ─────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# okf-coverage-check.sh — prove the OKF bundle documents EVERY required component.
|
|
3
|
+
#
|
|
4
|
+
# This is the gate that fixes "the wiki is not generated completely for all
|
|
5
|
+
# modules". It compares the deterministic expected set (concept-plan.json from
|
|
6
|
+
# okf-plan-concepts.sh) against the pages that actually exist in the bundle. A
|
|
7
|
+
# required concept with no page — or a present-but-empty page — fails the build,
|
|
8
|
+
# so a gappy bundle cannot be promoted (draft.tmp/ → draft/). Deferred entries
|
|
9
|
+
# must be reasoned in the generated coverage.md, never silently absent.
|
|
10
|
+
#
|
|
11
|
+
# Checks:
|
|
12
|
+
# C-PLAN every required plan entry has a bundle page (concept_id exists)
|
|
13
|
+
# C-STUB each satisfying page has real body content (>= --min-stub-lines)
|
|
14
|
+
# C-DEFER every deferred entry is recorded with a reason (always true: from plan)
|
|
15
|
+
#
|
|
16
|
+
# Side effect: regenerates <BUNDLE>/systems/coverage.md (tool-owned) unless
|
|
17
|
+
# --no-coverage-page is given.
|
|
18
|
+
#
|
|
19
|
+
# Usage:
|
|
20
|
+
# okf-coverage-check.sh --plan FILE --bundle DIR [--min-stub-lines N]
|
|
21
|
+
# [--no-coverage-page] [--json] [--report FILE]
|
|
22
|
+
#
|
|
23
|
+
# Exit codes: 0 complete, 1 incomplete (missing/stub required), 2 plan/bundle missing.
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
# shellcheck source=scripts/tools/_lib.sh
|
|
28
|
+
source "$SCRIPT_DIR/_lib.sh"
|
|
29
|
+
|
|
30
|
+
PLAN=""
|
|
31
|
+
BUNDLE=""
|
|
32
|
+
MIN_STUB_LINES=10
|
|
33
|
+
WRITE_PAGE=1
|
|
34
|
+
JSON=0
|
|
35
|
+
REPORT=""
|
|
36
|
+
|
|
37
|
+
usage() {
|
|
38
|
+
cat <<'EOF'
|
|
39
|
+
okf-coverage-check.sh — verify every required concept in the plan has a real page.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
okf-coverage-check.sh --plan FILE --bundle DIR [--min-stub-lines N]
|
|
43
|
+
[--no-coverage-page] [--json] [--report FILE]
|
|
44
|
+
|
|
45
|
+
Flags:
|
|
46
|
+
--plan FILE concept-plan.json from okf-plan-concepts.sh (required).
|
|
47
|
+
--bundle DIR The wiki/ bundle directory (required).
|
|
48
|
+
--min-stub-lines N Min non-blank body lines for a page to count as real (default 10).
|
|
49
|
+
--no-coverage-page Do not (re)write <BUNDLE>/systems/coverage.md.
|
|
50
|
+
--json Emit a JSON summary.
|
|
51
|
+
--report FILE Also write the JSON summary to FILE.
|
|
52
|
+
--help Show this help.
|
|
53
|
+
|
|
54
|
+
Exit: 0 complete, 1 incomplete, 2 plan/bundle not found.
|
|
55
|
+
EOF
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
while [[ $# -gt 0 ]]; do
|
|
59
|
+
case "$1" in
|
|
60
|
+
--plan) PLAN="$2"; shift 2;;
|
|
61
|
+
--bundle) BUNDLE="$2"; shift 2;;
|
|
62
|
+
--min-stub-lines) MIN_STUB_LINES="$2"; shift 2;;
|
|
63
|
+
--no-coverage-page) WRITE_PAGE=0; shift;;
|
|
64
|
+
--json) JSON=1; shift;;
|
|
65
|
+
--report) REPORT="$2"; shift 2;;
|
|
66
|
+
--help|-h) usage; exit 0;;
|
|
67
|
+
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
68
|
+
*) echo "Unexpected arg: $1" >&2; usage >&2; exit 1;;
|
|
69
|
+
esac
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
[[ -n "$PLAN" ]] || { usage >&2; exit 1; }
|
|
73
|
+
[[ -n "$BUNDLE" ]] || { usage >&2; exit 1; }
|
|
74
|
+
[[ -f "$PLAN" ]] || { echo "ERROR: plan not found: $PLAN" >&2; exit 2; }
|
|
75
|
+
[[ -d "$BUNDLE" ]] || { echo "ERROR: bundle directory not found: $BUNDLE" >&2; exit 2; }
|
|
76
|
+
command -v jq >/dev/null 2>&1 || { echo "ERROR: jq required" >&2; exit 2; }
|
|
77
|
+
BUNDLE="${BUNDLE%/}"
|
|
78
|
+
|
|
79
|
+
jq -e '.expected' "$PLAN" >/dev/null 2>&1 || { echo "ERROR: plan has no .expected array: $PLAN" >&2; exit 2; }
|
|
80
|
+
|
|
81
|
+
# Non-blank body line count for a page.
|
|
82
|
+
body_lines() {
|
|
83
|
+
awk 'NR==1&&/^---$/{fm=1;next} fm&&/^---$/{fm=0;next} !fm{print}' "$1" | grep -cE '.' || true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
MISSING=() # concept_id (required, no page)
|
|
87
|
+
STUB=() # concept_id\tlines (required, page too thin)
|
|
88
|
+
FULL=() # concept_id
|
|
89
|
+
DEFERRED=() # concept_id\treason
|
|
90
|
+
EXPECTED_TOTAL=0; REQUIRED=0
|
|
91
|
+
|
|
92
|
+
# Iterate expected entries.
|
|
93
|
+
while IFS=$'\t' read -r cid required reason ftype fanin; do
|
|
94
|
+
[[ -z "$cid" ]] && continue
|
|
95
|
+
EXPECTED_TOTAL=$((EXPECTED_TOTAL + 1))
|
|
96
|
+
if [[ "$required" == "true" ]]; then
|
|
97
|
+
REQUIRED=$((REQUIRED + 1))
|
|
98
|
+
if [[ -f "$BUNDLE/$cid" ]]; then
|
|
99
|
+
bl="$(body_lines "$BUNDLE/$cid")"
|
|
100
|
+
if [[ "$bl" -ge "$MIN_STUB_LINES" ]]; then
|
|
101
|
+
FULL+=("$cid")
|
|
102
|
+
else
|
|
103
|
+
STUB+=("$cid"$'\t'"$bl")
|
|
104
|
+
fi
|
|
105
|
+
else
|
|
106
|
+
MISSING+=("$cid")
|
|
107
|
+
fi
|
|
108
|
+
else
|
|
109
|
+
DEFERRED+=("$cid"$'\t'"${reason:-unspecified}")
|
|
110
|
+
fi
|
|
111
|
+
done < <(jq -r '.expected[] | [.concept_id, (.required|tostring), (.reason_if_deferred // "-"), (.type // "Module"), (.fan_in // 0 | tostring)] | @tsv' "$PLAN")
|
|
112
|
+
|
|
113
|
+
MAPPED=$(( ${#FULL[@]} ))
|
|
114
|
+
PASS=1
|
|
115
|
+
{ [[ ${#MISSING[@]} -gt 0 ]] || [[ ${#STUB[@]} -gt 0 ]]; } && PASS=0
|
|
116
|
+
PCT=100
|
|
117
|
+
[[ $REQUIRED -gt 0 ]] && PCT=$(( MAPPED * 100 / REQUIRED ))
|
|
118
|
+
|
|
119
|
+
# --- Generate coverage.md (tool-owned) ---
|
|
120
|
+
write_coverage_page() {
|
|
121
|
+
local out="$BUNDLE/systems/coverage.md"
|
|
122
|
+
mkdir -p "$BUNDLE/systems"
|
|
123
|
+
local tmp; tmp="$(mktemp)"
|
|
124
|
+
{
|
|
125
|
+
echo "<!-- okf:coverage-generated -->"
|
|
126
|
+
echo "# Component Coverage"
|
|
127
|
+
echo ""
|
|
128
|
+
echo "> Generated by \`okf-coverage-check.sh\` — do not hand-edit (except deferral reasons in the manifest)."
|
|
129
|
+
echo "> Required components documented: ${MAPPED}/${REQUIRED} (${PCT}%)."
|
|
130
|
+
echo ""
|
|
131
|
+
echo "| Component | Wiki page | Status | Fan-in |"
|
|
132
|
+
echo "|-----------|-----------|--------|--------|"
|
|
133
|
+
local cid status fanin
|
|
134
|
+
while IFS=$'\t' read -r cid required reason ftype fanin; do
|
|
135
|
+
[[ -z "$cid" ]] && continue
|
|
136
|
+
if [[ "$required" == "true" ]]; then
|
|
137
|
+
if [[ -f "$BUNDLE/$cid" ]]; then
|
|
138
|
+
bl="$(body_lines "$BUNDLE/$cid")"
|
|
139
|
+
if [[ "$bl" -ge "$MIN_STUB_LINES" ]]; then
|
|
140
|
+
echo "| \`${cid%.md}\` | [page](${cid#systems/}) | Full | ${fanin} |"
|
|
141
|
+
else
|
|
142
|
+
echo "| \`${cid%.md}\` | ${cid} | **STUB (${bl} lines)** | ${fanin} |"
|
|
143
|
+
fi
|
|
144
|
+
else
|
|
145
|
+
echo "| \`${cid%.md}\` | — | **MISSING** | ${fanin} |"
|
|
146
|
+
fi
|
|
147
|
+
else
|
|
148
|
+
echo "| \`${cid%.md}\` | — | Deferred (${reason:-unspecified}) | ${fanin} |"
|
|
149
|
+
fi
|
|
150
|
+
done < <(jq -r '.expected[] | [.concept_id, (.required|tostring), (.reason_if_deferred // "-"), (.type // "Module"), (.fan_in // 0 | tostring)] | @tsv' "$PLAN")
|
|
151
|
+
} > "$tmp"
|
|
152
|
+
mv "$tmp" "$out"
|
|
153
|
+
}
|
|
154
|
+
[[ $WRITE_PAGE -eq 1 ]] && write_coverage_page
|
|
155
|
+
|
|
156
|
+
# --- Report ---
|
|
157
|
+
emit_json() {
|
|
158
|
+
printf '{"valid":%s,"bundle":"%s","plan":"%s","required":%d,"mapped":%d,"coverage_pct":%d,"missing":[' \
|
|
159
|
+
"$([[ $PASS -eq 1 ]] && echo true || echo false)" \
|
|
160
|
+
"$(json_escape "$BUNDLE")" "$(json_escape "$PLAN")" "$REQUIRED" "$MAPPED" "$PCT"
|
|
161
|
+
for i in "${!MISSING[@]}"; do [[ $i -gt 0 ]] && printf ','; printf '"%s"' "$(json_escape "${MISSING[$i]}")"; done
|
|
162
|
+
printf '],"stub":['
|
|
163
|
+
local first=1
|
|
164
|
+
for s in "${STUB[@]:-}"; do
|
|
165
|
+
[[ -z "$s" ]] && continue
|
|
166
|
+
IFS=$'\t' read -r cid bl <<< "$s"
|
|
167
|
+
[[ $first -eq 1 ]] && first=0 || printf ','
|
|
168
|
+
printf '{"concept_id":"%s","lines":%d}' "$(json_escape "$cid")" "$bl"
|
|
169
|
+
done
|
|
170
|
+
printf '],"deferred":%d}\n' "${#DEFERRED[@]}"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if [[ -n "$REPORT" ]]; then mkdir -p "$(dirname "$REPORT")"; emit_json > "$REPORT"; fi
|
|
174
|
+
|
|
175
|
+
if [[ $JSON -eq 1 ]]; then
|
|
176
|
+
emit_json
|
|
177
|
+
else
|
|
178
|
+
if [[ $PASS -eq 1 ]]; then
|
|
179
|
+
echo "OKF coverage complete: ${MAPPED}/${REQUIRED} required components (${PCT}%), ${#DEFERRED[@]} deferred"
|
|
180
|
+
else
|
|
181
|
+
echo "OKF coverage INCOMPLETE: ${MAPPED}/${REQUIRED} required (${PCT}%)" >&2
|
|
182
|
+
for m in "${MISSING[@]:-}"; do [[ -n "$m" ]] && echo " - MISSING: $m" >&2; done
|
|
183
|
+
for s in "${STUB[@]:-}"; do
|
|
184
|
+
[[ -z "$s" ]] && continue
|
|
185
|
+
IFS=$'\t' read -r cid bl <<< "$s"
|
|
186
|
+
echo " - STUB: $cid ($bl body lines < $MIN_STUB_LINES)" >&2
|
|
187
|
+
done
|
|
188
|
+
fi
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
[[ $PASS -eq 1 ]] || exit 1
|
|
192
|
+
exit 0
|