@drafthq/draft 3.5.1 → 3.5.3
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/ai-context-index.md +1 -1
- package/core/templates/okf/index.md +9 -13
- package/core/templates/okf/section-index.md +10 -19
- package/integrations/agents/AGENTS.md +54 -44
- package/integrations/copilot/.github/copilot-instructions.md +54 -44
- package/package.json +1 -1
- package/scripts/tools/okf-coverage-check.sh +20 -2
- package/scripts/tools/okf-plan-concepts.sh +14 -3
- package/scripts/tools/okf-render-views.sh +77 -6
- package/scripts/tools/okf-validate-quality.sh +40 -10
- package/scripts/tools/okf-validate.sh +64 -2
- package/skills/init/SKILL.md +1 -1
- package/skills/init/references/okf-emitter.md +33 -10
|
@@ -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.5.
|
|
15
|
+
"version": "3.5.3",
|
|
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.
|
|
5
|
+
"version": "3.5.3",
|
|
6
6
|
"skills": "./skills/",
|
|
7
7
|
"agents": "./core/agents/",
|
|
8
8
|
"author": {
|
|
@@ -8,7 +8,7 @@ draft_init_mode: okf
|
|
|
8
8
|
|
|
9
9
|
# {PROJECT_NAME} — AI Context Index
|
|
10
10
|
|
|
11
|
-
> Index root for the
|
|
11
|
+
> Index root for the project wiki (`wiki/`). Read **Synopsis** for broad
|
|
12
12
|
> tasks (they usually terminate here). For focused tasks, route through the
|
|
13
13
|
> **Concept Map** to ≤N concept pages — each lists `x-grounded-paths`. This is
|
|
14
14
|
> both the cheap broad-context path AND the progressive-disclosure entry point.
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
type: Subsystem
|
|
3
|
-
title: "{PROJECT_NAME} — Knowledge Bundle"
|
|
4
|
-
description: >
|
|
5
|
-
Root index of the OKF taxonomy bundle. Start here, then route into
|
|
6
|
-
overview/, systems/, features/, reference/, or entrypoints/ via the
|
|
7
|
-
Concept Map. Open a concept only when its description matches the task.
|
|
8
|
-
resource: .
|
|
9
|
-
tags: [index]
|
|
10
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
11
2
|
okf_version: "0.1"
|
|
12
|
-
okf_types_version: "0.1"
|
|
13
3
|
---
|
|
14
4
|
|
|
15
|
-
|
|
5
|
+
<!-- OKF §6/§11: an index file carries NO concept frontmatter; the root index.md
|
|
6
|
+
may declare ONLY `okf_version`. The frozen concept-`type` vocabulary version
|
|
7
|
+
is tracked here in the body (not in frontmatter) so bumping it stays visible
|
|
8
|
+
without violating the index-frontmatter rule.
|
|
9
|
+
okf-types-version: 0.1 -->
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
# {PROJECT_NAME} — Wiki
|
|
12
|
+
|
|
13
|
+
> Project wiki. One concept per file; cross-links form the graph. The
|
|
14
|
+
> live call graph (`codebase-memory-mcp`) is the grounding source; this wiki
|
|
19
15
|
> is the navigable serialization. `../ai-context.md` is the consumption entry point.
|
|
20
16
|
|
|
21
17
|
## Sections
|
|
@@ -1,25 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
title: "{SECTION_TITLE}"
|
|
4
|
-
description: >
|
|
5
|
-
Section index. Lists every concept in this section with its one-line
|
|
6
|
-
routing description so an agent can pick the right page without opening
|
|
7
|
-
each one. {SECTION_PURPOSE}
|
|
8
|
-
resource: .
|
|
9
|
-
tags: [index]
|
|
10
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
11
|
-
---
|
|
1
|
+
<!-- OKF §6: an index file contains NO frontmatter. This page is a reserved
|
|
2
|
+
navigation index, not a concept — its Concepts table is tool-generated. -->
|
|
12
3
|
|
|
13
4
|
# {SECTION_TITLE}
|
|
14
5
|
|
|
15
|
-
> Section of the
|
|
6
|
+
> Section of the project wiki. Back to [wiki root](../index.md).
|
|
16
7
|
|
|
17
8
|
## Concepts
|
|
18
9
|
|
|
19
|
-
<!--
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
<!-- TOOL-OWNED: regenerated deterministically by okf-render-views.sh
|
|
11
|
+
(--section-indexes) from the concept pages in this directory. One row per
|
|
12
|
+
page; `description` is the routing key copied from each page's frontmatter.
|
|
13
|
+
Do not hand-edit between the markers — links here are guaranteed to resolve
|
|
14
|
+
because they are built only from files that actually exist. -->
|
|
15
|
+
<!-- CONCEPT-MAP:START -->
|
|
16
|
+
<!-- CONCEPT-MAP:END -->
|
|
@@ -341,7 +341,7 @@ The tier-gated default rests on **maintainability/readability** (one navigable c
|
|
|
341
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
342
|
2. Every `required` entry in `concept-plan.json` has a non-stub page.
|
|
343
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) —
|
|
344
|
+
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) — **every** package/module the graph found is required by default (the fan-in floor only types Subsystem-vs-Module, it does not exempt), so none may be **MISSING**. Section `index.md` tables are regenerated by `okf-render-views.sh --section-indexes`, never hand-authored, so their links cannot dangle.
|
|
345
345
|
5. On any failure: **do not** atomic-rename; surface `.state/validation-report.json`.
|
|
346
346
|
|
|
347
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.
|
|
@@ -3353,7 +3353,8 @@ Templates for each bundle page live in `core/templates/okf/` (`index.md`,
|
|
|
3353
3353
|
## Frozen `type` vocabulary
|
|
3354
3354
|
|
|
3355
3355
|
Every concept carries a `type` from this frozen set (changing it churns every
|
|
3356
|
-
file; versioned via `
|
|
3356
|
+
file; versioned via the `okf-types-version` comment in the wiki root `index.md`
|
|
3357
|
+
body — OKF §6/§11 permit only `okf_version` in an index file's frontmatter):
|
|
3357
3358
|
|
|
3358
3359
|
| type | Maps to | Home |
|
|
3359
3360
|
|------|---------|------|
|
|
@@ -3379,6 +3380,16 @@ the task at hand?"), never a summary. Draft extensions are namespaced `x-` and
|
|
|
3379
3380
|
ignored by generic OKF consumers: `x-grounded-paths`, `x-hotspot-score`,
|
|
3380
3381
|
`x-callers`.
|
|
3381
3382
|
|
|
3383
|
+
**Meta pages are not concepts.** Index files (`wiki/index.md` and every
|
|
3384
|
+
`<section>/index.md`) carry **no concept frontmatter**: per OKF §6 an index file
|
|
3385
|
+
has no frontmatter, and per §11 the root `index.md` may declare **only**
|
|
3386
|
+
`okf_version`. The tool-generated `systems/coverage.md` is a non-reserved file, so
|
|
3387
|
+
OKF §9.1/§9.2 require it to carry a typed frontmatter block; it uses a descriptive
|
|
3388
|
+
`type: Report` and is exempt from the frozen vocabulary via `is_meta_page`
|
|
3389
|
+
(basename + the `<!-- okf:coverage-generated -->` marker). `okf-validate.sh`
|
|
3390
|
+
short-circuits all meta pages before the concept checks, so they are never
|
|
3391
|
+
vocab-checked or counted as concepts.
|
|
3392
|
+
|
|
3382
3393
|
## Concept granularity (resolves open decision 1)
|
|
3383
3394
|
|
|
3384
3395
|
Derive concepts from the graph, not by hand:
|
|
@@ -3398,17 +3409,21 @@ Derive concepts from the graph, not by hand:
|
|
|
3398
3409
|
```
|
|
3399
3410
|
1. Survey → existing draft init 5-phase + graph snapshot (graph-snapshot.sh)
|
|
3400
3411
|
2. Plan → DETERMINISTIC. okf-plan-concepts.sh derives the expected-concept
|
|
3401
|
-
set from the graph
|
|
3402
|
-
|
|
3403
|
-
|
|
3412
|
+
set from the graph. EVERY package the graph knows about is
|
|
3413
|
+
required (fan_in ≥ floor → Subsystem; below floor → Module — the
|
|
3414
|
+
floor only types/orders, it never exempts); entrypoints → required;
|
|
3415
|
+
only --allow-defer matches are deferred (with a reason). Writes
|
|
3416
|
+
draft.tmp/.state/concept-plan.json.
|
|
3404
3417
|
okf-plan-concepts.sh --repo . [--scope PATH] \
|
|
3405
3418
|
[--manifest FILE] [--min-fan-in 2] [--allow-defer GLOB]... \
|
|
3406
3419
|
--out draft.tmp/.state/concept-plan.json
|
|
3407
3420
|
This replaces the old in-context concept enumeration — the boundary
|
|
3408
3421
|
of the work is now a tool output, not an LLM judgment, so modules
|
|
3409
|
-
cannot be silently dropped.
|
|
3410
|
-
|
|
3411
|
-
(required
|
|
3422
|
+
and sub-modules cannot be silently dropped. (Legacy fan-in
|
|
3423
|
+
exemption is opt-in via --defer-below-floor.) LOG the counts
|
|
3424
|
+
(expected/required/deferred) BEFORE writing any page.
|
|
3425
|
+
`generated_order` is topo-ish (required + high-fan-in first) so
|
|
3426
|
+
forward cross-links resolve.
|
|
3412
3427
|
3. Generate → iterate concept-plan.generated_order; write ONE page per REQUIRED
|
|
3413
3428
|
entry, grounding each from the graph:
|
|
3414
3429
|
x-callers ← graph-callers.sh --symbol <c>
|
|
@@ -3428,9 +3443,11 @@ Derive concepts from the graph, not by hand:
|
|
|
3428
3443
|
--plan draft.tmp/.state/concept-plan.json \
|
|
3429
3444
|
--path-index draft.tmp/.state/path-to-concept.json \
|
|
3430
3445
|
--strict --report draft.tmp/.state/validation-report.json
|
|
3431
|
-
It runs, in order: okf-validate.sh (structure + reverse index
|
|
3432
|
-
|
|
3433
|
-
okf-
|
|
3446
|
+
It runs, in order: okf-validate.sh (structure + reverse index +
|
|
3447
|
+
empty/untyped-page + leftover-template-token + dangling-link checks),
|
|
3448
|
+
okf-validate-quality.sh (per-type anti-stub / depth / per-section
|
|
3449
|
+
content / mermaid lint), okf-coverage-check.sh (every required plan
|
|
3450
|
+
entry → real page).
|
|
3434
3451
|
ANY layer failing ⇒ exit non-zero ⇒ DO NOT atomic-rename.
|
|
3435
3452
|
coverage.md (systems/coverage.md) is regenerated by the coverage
|
|
3436
3453
|
layer; it is tool-owned (marker <!-- okf:coverage-generated -->) —
|
|
@@ -3469,6 +3486,7 @@ regenerated on every init/refresh so they never drift from the bundle:
|
|
|
3469
3486
|
```bash
|
|
3470
3487
|
okf-render-views.sh draft/wiki \
|
|
3471
3488
|
--arch-out draft/architecture.md \
|
|
3489
|
+
--section-indexes \
|
|
3472
3490
|
--concept-map-into draft/wiki/index.md \
|
|
3473
3491
|
--concept-map-into draft/.ai-context.md \
|
|
3474
3492
|
--web draft/wiki/web/index.html
|
|
@@ -3476,6 +3494,11 @@ okf-render-views.sh draft/wiki \
|
|
|
3476
3494
|
|
|
3477
3495
|
- `--arch-out` renders the linear `architecture.md` (banner + TOC + every concept
|
|
3478
3496
|
page in canonical section order, frontmatter stripped, Mermaid preserved).
|
|
3497
|
+
- `--section-indexes` rebuilds each `<section>/index.md` concept table (between its
|
|
3498
|
+
`CONCEPT-MAP` markers) from the pages that actually exist in that directory. This
|
|
3499
|
+
is mandatory: section indexes are NOT hand-authored — building them from real
|
|
3500
|
+
files is what makes their links impossible to dangle. Never write a section
|
|
3501
|
+
index "Concepts" table by hand.
|
|
3479
3502
|
- `--concept-map-into` rebuilds the routing table between the
|
|
3480
3503
|
`<!-- CONCEPT-MAP:START -->` / `:END` markers from each concept's `title` +
|
|
3481
3504
|
`type` + `description` (section `index.md` pages excluded).
|
|
@@ -22641,23 +22664,19 @@ List down alerting thresholds on those metrics:
|
|
|
22641
22664
|
<core-file path="core/templates/okf/index.md">
|
|
22642
22665
|
|
|
22643
22666
|
---
|
|
22644
|
-
type: Subsystem
|
|
22645
|
-
title: "{PROJECT_NAME} — Knowledge Bundle"
|
|
22646
|
-
description: >
|
|
22647
|
-
Root index of the OKF taxonomy bundle. Start here, then route into
|
|
22648
|
-
overview/, systems/, features/, reference/, or entrypoints/ via the
|
|
22649
|
-
Concept Map. Open a concept only when its description matches the task.
|
|
22650
|
-
resource: .
|
|
22651
|
-
tags: [index]
|
|
22652
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
22653
22667
|
okf_version: "0.1"
|
|
22654
|
-
okf_types_version: "0.1"
|
|
22655
22668
|
---
|
|
22656
22669
|
|
|
22657
|
-
|
|
22670
|
+
<!-- OKF §6/§11: an index file carries NO concept frontmatter; the root index.md
|
|
22671
|
+
may declare ONLY `okf_version`. The frozen concept-`type` vocabulary version
|
|
22672
|
+
is tracked here in the body (not in frontmatter) so bumping it stays visible
|
|
22673
|
+
without violating the index-frontmatter rule.
|
|
22674
|
+
okf-types-version: 0.1 -->
|
|
22675
|
+
|
|
22676
|
+
# {PROJECT_NAME} — Wiki
|
|
22658
22677
|
|
|
22659
|
-
>
|
|
22660
|
-
> live call graph (`codebase-memory-mcp`) is the grounding source; this
|
|
22678
|
+
> Project wiki. One concept per file; cross-links form the graph. The
|
|
22679
|
+
> live call graph (`codebase-memory-mcp`) is the grounding source; this wiki
|
|
22661
22680
|
> is the navigable serialization. `../ai-context.md` is the consumption entry point.
|
|
22662
22681
|
|
|
22663
22682
|
## Sections
|
|
@@ -22761,31 +22780,22 @@ Entrypoint) require ≥1 valid Mermaid block and ≥2 x-grounded-paths.
|
|
|
22761
22780
|
|
|
22762
22781
|
<core-file path="core/templates/okf/section-index.md">
|
|
22763
22782
|
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
title: "{SECTION_TITLE}"
|
|
22767
|
-
description: >
|
|
22768
|
-
Section index. Lists every concept in this section with its one-line
|
|
22769
|
-
routing description so an agent can pick the right page without opening
|
|
22770
|
-
each one. {SECTION_PURPOSE}
|
|
22771
|
-
resource: .
|
|
22772
|
-
tags: [index]
|
|
22773
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
22774
|
-
---
|
|
22783
|
+
<!-- OKF §6: an index file contains NO frontmatter. This page is a reserved
|
|
22784
|
+
navigation index, not a concept — its Concepts table is tool-generated. -->
|
|
22775
22785
|
|
|
22776
22786
|
# {SECTION_TITLE}
|
|
22777
22787
|
|
|
22778
|
-
> Section of the
|
|
22788
|
+
> Section of the project wiki. Back to [wiki root](../index.md).
|
|
22779
22789
|
|
|
22780
22790
|
## Concepts
|
|
22781
22791
|
|
|
22782
|
-
<!--
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22792
|
+
<!-- TOOL-OWNED: regenerated deterministically by okf-render-views.sh
|
|
22793
|
+
(--section-indexes) from the concept pages in this directory. One row per
|
|
22794
|
+
page; `description` is the routing key copied from each page's frontmatter.
|
|
22795
|
+
Do not hand-edit between the markers — links here are guaranteed to resolve
|
|
22796
|
+
because they are built only from files that actually exist. -->
|
|
22797
|
+
<!-- CONCEPT-MAP:START -->
|
|
22798
|
+
<!-- CONCEPT-MAP:END -->
|
|
22789
22799
|
|
|
22790
22800
|
</core-file>
|
|
22791
22801
|
|
|
@@ -22805,7 +22815,7 @@ draft_init_mode: okf
|
|
|
22805
22815
|
|
|
22806
22816
|
# {PROJECT_NAME} — AI Context Index
|
|
22807
22817
|
|
|
22808
|
-
> Index root for the
|
|
22818
|
+
> Index root for the project wiki (`wiki/`). Read **Synopsis** for broad
|
|
22809
22819
|
> tasks (they usually terminate here). For focused tasks, route through the
|
|
22810
22820
|
> **Concept Map** to ≤N concept pages — each lists `x-grounded-paths`. This is
|
|
22811
22821
|
> both the cheap broad-context path AND the progressive-disclosure entry point.
|
|
@@ -341,7 +341,7 @@ The tier-gated default rests on **maintainability/readability** (one navigable c
|
|
|
341
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
342
|
2. Every `required` entry in `concept-plan.json` has a non-stub page.
|
|
343
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) —
|
|
344
|
+
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) — **every** package/module the graph found is required by default (the fan-in floor only types Subsystem-vs-Module, it does not exempt), so none may be **MISSING**. Section `index.md` tables are regenerated by `okf-render-views.sh --section-indexes`, never hand-authored, so their links cannot dangle.
|
|
345
345
|
5. On any failure: **do not** atomic-rename; surface `.state/validation-report.json`.
|
|
346
346
|
|
|
347
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.
|
|
@@ -3353,7 +3353,8 @@ Templates for each bundle page live in `core/templates/okf/` (`index.md`,
|
|
|
3353
3353
|
## Frozen `type` vocabulary
|
|
3354
3354
|
|
|
3355
3355
|
Every concept carries a `type` from this frozen set (changing it churns every
|
|
3356
|
-
file; versioned via `
|
|
3356
|
+
file; versioned via the `okf-types-version` comment in the wiki root `index.md`
|
|
3357
|
+
body — OKF §6/§11 permit only `okf_version` in an index file's frontmatter):
|
|
3357
3358
|
|
|
3358
3359
|
| type | Maps to | Home |
|
|
3359
3360
|
|------|---------|------|
|
|
@@ -3379,6 +3380,16 @@ the task at hand?"), never a summary. Draft extensions are namespaced `x-` and
|
|
|
3379
3380
|
ignored by generic OKF consumers: `x-grounded-paths`, `x-hotspot-score`,
|
|
3380
3381
|
`x-callers`.
|
|
3381
3382
|
|
|
3383
|
+
**Meta pages are not concepts.** Index files (`wiki/index.md` and every
|
|
3384
|
+
`<section>/index.md`) carry **no concept frontmatter**: per OKF §6 an index file
|
|
3385
|
+
has no frontmatter, and per §11 the root `index.md` may declare **only**
|
|
3386
|
+
`okf_version`. The tool-generated `systems/coverage.md` is a non-reserved file, so
|
|
3387
|
+
OKF §9.1/§9.2 require it to carry a typed frontmatter block; it uses a descriptive
|
|
3388
|
+
`type: Report` and is exempt from the frozen vocabulary via `is_meta_page`
|
|
3389
|
+
(basename + the `<!-- okf:coverage-generated -->` marker). `okf-validate.sh`
|
|
3390
|
+
short-circuits all meta pages before the concept checks, so they are never
|
|
3391
|
+
vocab-checked or counted as concepts.
|
|
3392
|
+
|
|
3382
3393
|
## Concept granularity (resolves open decision 1)
|
|
3383
3394
|
|
|
3384
3395
|
Derive concepts from the graph, not by hand:
|
|
@@ -3398,17 +3409,21 @@ Derive concepts from the graph, not by hand:
|
|
|
3398
3409
|
```
|
|
3399
3410
|
1. Survey → existing draft init 5-phase + graph snapshot (graph-snapshot.sh)
|
|
3400
3411
|
2. Plan → DETERMINISTIC. okf-plan-concepts.sh derives the expected-concept
|
|
3401
|
-
set from the graph
|
|
3402
|
-
|
|
3403
|
-
|
|
3412
|
+
set from the graph. EVERY package the graph knows about is
|
|
3413
|
+
required (fan_in ≥ floor → Subsystem; below floor → Module — the
|
|
3414
|
+
floor only types/orders, it never exempts); entrypoints → required;
|
|
3415
|
+
only --allow-defer matches are deferred (with a reason). Writes
|
|
3416
|
+
draft.tmp/.state/concept-plan.json.
|
|
3404
3417
|
okf-plan-concepts.sh --repo . [--scope PATH] \
|
|
3405
3418
|
[--manifest FILE] [--min-fan-in 2] [--allow-defer GLOB]... \
|
|
3406
3419
|
--out draft.tmp/.state/concept-plan.json
|
|
3407
3420
|
This replaces the old in-context concept enumeration — the boundary
|
|
3408
3421
|
of the work is now a tool output, not an LLM judgment, so modules
|
|
3409
|
-
cannot be silently dropped.
|
|
3410
|
-
|
|
3411
|
-
(required
|
|
3422
|
+
and sub-modules cannot be silently dropped. (Legacy fan-in
|
|
3423
|
+
exemption is opt-in via --defer-below-floor.) LOG the counts
|
|
3424
|
+
(expected/required/deferred) BEFORE writing any page.
|
|
3425
|
+
`generated_order` is topo-ish (required + high-fan-in first) so
|
|
3426
|
+
forward cross-links resolve.
|
|
3412
3427
|
3. Generate → iterate concept-plan.generated_order; write ONE page per REQUIRED
|
|
3413
3428
|
entry, grounding each from the graph:
|
|
3414
3429
|
x-callers ← graph-callers.sh --symbol <c>
|
|
@@ -3428,9 +3443,11 @@ Derive concepts from the graph, not by hand:
|
|
|
3428
3443
|
--plan draft.tmp/.state/concept-plan.json \
|
|
3429
3444
|
--path-index draft.tmp/.state/path-to-concept.json \
|
|
3430
3445
|
--strict --report draft.tmp/.state/validation-report.json
|
|
3431
|
-
It runs, in order: okf-validate.sh (structure + reverse index
|
|
3432
|
-
|
|
3433
|
-
okf-
|
|
3446
|
+
It runs, in order: okf-validate.sh (structure + reverse index +
|
|
3447
|
+
empty/untyped-page + leftover-template-token + dangling-link checks),
|
|
3448
|
+
okf-validate-quality.sh (per-type anti-stub / depth / per-section
|
|
3449
|
+
content / mermaid lint), okf-coverage-check.sh (every required plan
|
|
3450
|
+
entry → real page).
|
|
3434
3451
|
ANY layer failing ⇒ exit non-zero ⇒ DO NOT atomic-rename.
|
|
3435
3452
|
coverage.md (systems/coverage.md) is regenerated by the coverage
|
|
3436
3453
|
layer; it is tool-owned (marker <!-- okf:coverage-generated -->) —
|
|
@@ -3469,6 +3486,7 @@ regenerated on every init/refresh so they never drift from the bundle:
|
|
|
3469
3486
|
```bash
|
|
3470
3487
|
okf-render-views.sh draft/wiki \
|
|
3471
3488
|
--arch-out draft/architecture.md \
|
|
3489
|
+
--section-indexes \
|
|
3472
3490
|
--concept-map-into draft/wiki/index.md \
|
|
3473
3491
|
--concept-map-into draft/.ai-context.md \
|
|
3474
3492
|
--web draft/wiki/web/index.html
|
|
@@ -3476,6 +3494,11 @@ okf-render-views.sh draft/wiki \
|
|
|
3476
3494
|
|
|
3477
3495
|
- `--arch-out` renders the linear `architecture.md` (banner + TOC + every concept
|
|
3478
3496
|
page in canonical section order, frontmatter stripped, Mermaid preserved).
|
|
3497
|
+
- `--section-indexes` rebuilds each `<section>/index.md` concept table (between its
|
|
3498
|
+
`CONCEPT-MAP` markers) from the pages that actually exist in that directory. This
|
|
3499
|
+
is mandatory: section indexes are NOT hand-authored — building them from real
|
|
3500
|
+
files is what makes their links impossible to dangle. Never write a section
|
|
3501
|
+
index "Concepts" table by hand.
|
|
3479
3502
|
- `--concept-map-into` rebuilds the routing table between the
|
|
3480
3503
|
`<!-- CONCEPT-MAP:START -->` / `:END` markers from each concept's `title` +
|
|
3481
3504
|
`type` + `description` (section `index.md` pages excluded).
|
|
@@ -22641,23 +22664,19 @@ List down alerting thresholds on those metrics:
|
|
|
22641
22664
|
<core-file path="core/templates/okf/index.md">
|
|
22642
22665
|
|
|
22643
22666
|
---
|
|
22644
|
-
type: Subsystem
|
|
22645
|
-
title: "{PROJECT_NAME} — Knowledge Bundle"
|
|
22646
|
-
description: >
|
|
22647
|
-
Root index of the OKF taxonomy bundle. Start here, then route into
|
|
22648
|
-
overview/, systems/, features/, reference/, or entrypoints/ via the
|
|
22649
|
-
Concept Map. Open a concept only when its description matches the task.
|
|
22650
|
-
resource: .
|
|
22651
|
-
tags: [index]
|
|
22652
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
22653
22667
|
okf_version: "0.1"
|
|
22654
|
-
okf_types_version: "0.1"
|
|
22655
22668
|
---
|
|
22656
22669
|
|
|
22657
|
-
|
|
22670
|
+
<!-- OKF §6/§11: an index file carries NO concept frontmatter; the root index.md
|
|
22671
|
+
may declare ONLY `okf_version`. The frozen concept-`type` vocabulary version
|
|
22672
|
+
is tracked here in the body (not in frontmatter) so bumping it stays visible
|
|
22673
|
+
without violating the index-frontmatter rule.
|
|
22674
|
+
okf-types-version: 0.1 -->
|
|
22675
|
+
|
|
22676
|
+
# {PROJECT_NAME} — Wiki
|
|
22658
22677
|
|
|
22659
|
-
>
|
|
22660
|
-
> live call graph (`codebase-memory-mcp`) is the grounding source; this
|
|
22678
|
+
> Project wiki. One concept per file; cross-links form the graph. The
|
|
22679
|
+
> live call graph (`codebase-memory-mcp`) is the grounding source; this wiki
|
|
22661
22680
|
> is the navigable serialization. `../ai-context.md` is the consumption entry point.
|
|
22662
22681
|
|
|
22663
22682
|
## Sections
|
|
@@ -22761,31 +22780,22 @@ Entrypoint) require ≥1 valid Mermaid block and ≥2 x-grounded-paths.
|
|
|
22761
22780
|
|
|
22762
22781
|
<core-file path="core/templates/okf/section-index.md">
|
|
22763
22782
|
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
title: "{SECTION_TITLE}"
|
|
22767
|
-
description: >
|
|
22768
|
-
Section index. Lists every concept in this section with its one-line
|
|
22769
|
-
routing description so an agent can pick the right page without opening
|
|
22770
|
-
each one. {SECTION_PURPOSE}
|
|
22771
|
-
resource: .
|
|
22772
|
-
tags: [index]
|
|
22773
|
-
timestamp: "{ISO_TIMESTAMP}"
|
|
22774
|
-
---
|
|
22783
|
+
<!-- OKF §6: an index file contains NO frontmatter. This page is a reserved
|
|
22784
|
+
navigation index, not a concept — its Concepts table is tool-generated. -->
|
|
22775
22785
|
|
|
22776
22786
|
# {SECTION_TITLE}
|
|
22777
22787
|
|
|
22778
|
-
> Section of the
|
|
22788
|
+
> Section of the project wiki. Back to [wiki root](../index.md).
|
|
22779
22789
|
|
|
22780
22790
|
## Concepts
|
|
22781
22791
|
|
|
22782
|
-
<!--
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22792
|
+
<!-- TOOL-OWNED: regenerated deterministically by okf-render-views.sh
|
|
22793
|
+
(--section-indexes) from the concept pages in this directory. One row per
|
|
22794
|
+
page; `description` is the routing key copied from each page's frontmatter.
|
|
22795
|
+
Do not hand-edit between the markers — links here are guaranteed to resolve
|
|
22796
|
+
because they are built only from files that actually exist. -->
|
|
22797
|
+
<!-- CONCEPT-MAP:START -->
|
|
22798
|
+
<!-- CONCEPT-MAP:END -->
|
|
22789
22799
|
|
|
22790
22800
|
</core-file>
|
|
22791
22801
|
|
|
@@ -22805,7 +22815,7 @@ draft_init_mode: okf
|
|
|
22805
22815
|
|
|
22806
22816
|
# {PROJECT_NAME} — AI Context Index
|
|
22807
22817
|
|
|
22808
|
-
> Index root for the
|
|
22818
|
+
> Index root for the project wiki (`wiki/`). Read **Synopsis** for broad
|
|
22809
22819
|
> tasks (they usually terminate here). For focused tasks, route through the
|
|
22810
22820
|
> **Concept Map** to ≤N concept pages — each lists `x-grounded-paths`. This is
|
|
22811
22821
|
> both the cheap broad-context path AND the progressive-disclosure entry point.
|
package/package.json
CHANGED
|
@@ -122,6 +122,17 @@ write_coverage_page() {
|
|
|
122
122
|
mkdir -p "$BUNDLE/systems"
|
|
123
123
|
local tmp; tmp="$(mktemp)"
|
|
124
124
|
{
|
|
125
|
+
# OKF §9.1/§9.2: every non-reserved .md needs parseable frontmatter with a
|
|
126
|
+
# non-empty `type`. coverage.md is tool-generated and not a code concept, so
|
|
127
|
+
# it uses a descriptive (non-frozen) type; okf-validate.sh exempts it from the
|
|
128
|
+
# frozen-vocab check via is_meta_page (basename + the marker below).
|
|
129
|
+
echo "---"
|
|
130
|
+
echo "type: Report"
|
|
131
|
+
echo "title: Component Coverage"
|
|
132
|
+
echo "description: Coverage of required components by wiki pages — which are documented, stubbed, or missing."
|
|
133
|
+
echo "resource: ."
|
|
134
|
+
echo "---"
|
|
135
|
+
echo ""
|
|
125
136
|
echo "<!-- okf:coverage-generated -->"
|
|
126
137
|
echo "# Component Coverage"
|
|
127
138
|
echo ""
|
|
@@ -130,14 +141,21 @@ write_coverage_page() {
|
|
|
130
141
|
echo ""
|
|
131
142
|
echo "| Component | Wiki page | Status | Fan-in |"
|
|
132
143
|
echo "|-----------|-----------|--------|--------|"
|
|
133
|
-
local cid status fanin
|
|
144
|
+
local cid status fanin link
|
|
134
145
|
while IFS=$'\t' read -r cid required reason ftype fanin; do
|
|
135
146
|
[[ -z "$cid" ]] && continue
|
|
136
147
|
if [[ "$required" == "true" ]]; then
|
|
137
148
|
if [[ -f "$BUNDLE/$cid" ]]; then
|
|
138
149
|
bl="$(body_lines "$BUNDLE/$cid")"
|
|
150
|
+
# coverage.md lives in systems/; link relative to it. Concepts in
|
|
151
|
+
# other sections (entrypoints/, reference/, …) need a ../ prefix,
|
|
152
|
+
# otherwise the link dangles and fails structure validation.
|
|
153
|
+
case "$cid" in
|
|
154
|
+
systems/*) link="${cid#systems/}";;
|
|
155
|
+
*) link="../$cid";;
|
|
156
|
+
esac
|
|
139
157
|
if [[ "$bl" -ge "$MIN_STUB_LINES" ]]; then
|
|
140
|
-
echo "| \`${cid%.md}\` | [page](${
|
|
158
|
+
echo "| \`${cid%.md}\` | [page](${link}) | Full | ${fanin} |"
|
|
141
159
|
else
|
|
142
160
|
echo "| \`${cid%.md}\` | ${cid} | **STUB (${bl} lines)** | ${fanin} |"
|
|
143
161
|
fi
|
|
@@ -38,6 +38,7 @@ MANIFEST=""
|
|
|
38
38
|
MIN_FAN_IN=2
|
|
39
39
|
OUT=""
|
|
40
40
|
JSON=0
|
|
41
|
+
DEFER_BELOW_FLOOR=0
|
|
41
42
|
ALLOW_DEFER=()
|
|
42
43
|
|
|
43
44
|
usage() {
|
|
@@ -54,8 +55,12 @@ Flags:
|
|
|
54
55
|
--manifest FILE Component list (one component per line; '#' comments; blanks
|
|
55
56
|
ignored). When present it is authoritative — every entry is
|
|
56
57
|
required and the graph is not consulted.
|
|
57
|
-
--min-fan-in N
|
|
58
|
-
|
|
58
|
+
--min-fan-in N Fan-in threshold that types a package as a Subsystem (>=N)
|
|
59
|
+
vs a Module (<N) and orders it first (default: 2). By default
|
|
60
|
+
EVERY graph package is required regardless of fan-in — the
|
|
61
|
+
floor no longer exempts anything.
|
|
62
|
+
--defer-below-floor Restore the old behavior: packages with fan_in < --min-fan-in
|
|
63
|
+
are deferred (not required) instead of required-as-Module.
|
|
59
64
|
--allow-defer GLOB Defer (don't require) components whose name matches GLOB.
|
|
60
65
|
Repeatable. Deferred entries still appear in the plan.
|
|
61
66
|
--out FILE Write the plan JSON here (default: stdout).
|
|
@@ -72,6 +77,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
72
77
|
--scope) SCOPE="$2"; shift 2;;
|
|
73
78
|
--manifest) MANIFEST="$2"; shift 2;;
|
|
74
79
|
--min-fan-in) MIN_FAN_IN="$2"; shift 2;;
|
|
80
|
+
--defer-below-floor) DEFER_BELOW_FLOOR=1; shift;;
|
|
75
81
|
--allow-defer) ALLOW_DEFER+=("$2"); shift 2;;
|
|
76
82
|
--out) OUT="$2"; shift 2;;
|
|
77
83
|
--json) JSON=1; shift;;
|
|
@@ -150,8 +156,13 @@ plan_from_graph() {
|
|
|
150
156
|
required=false; reason="allow-defer match"; type=Module
|
|
151
157
|
elif (( fan_in >= MIN_FAN_IN )); then
|
|
152
158
|
required=true; reason=""; type=Subsystem
|
|
153
|
-
|
|
159
|
+
elif [[ $DEFER_BELOW_FLOOR -eq 1 ]]; then
|
|
160
|
+
# Opt-in legacy behavior: low-fan-in packages are exempted.
|
|
154
161
|
required=false; reason="fan_in $fan_in < floor $MIN_FAN_IN"; type=Module
|
|
162
|
+
else
|
|
163
|
+
# Default: every package the graph knows about is documented. Fan-in
|
|
164
|
+
# below the floor only demotes Subsystem→Module; it never exempts.
|
|
165
|
+
required=true; reason=""; type=Module
|
|
155
166
|
fi
|
|
156
167
|
add_concept "$name" systems "$type" "$name" "$fan_in" "$required" "$reason"
|
|
157
168
|
done < <(echo "$arch" | jq -r '.packages[]? | [.name, (.fan_in // 0)] | @tsv')
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
# 2. Concept Map — a routing table injected between the
|
|
11
11
|
# <!-- CONCEPT-MAP:START --> / <!-- CONCEPT-MAP:END --> markers in
|
|
12
12
|
# wiki/index.md (and optionally another index-root file).
|
|
13
|
+
# 3. Section indexes — (--section-indexes) each <section>/index.md concept
|
|
14
|
+
# table rebuilt from the pages that actually exist in that directory, so its
|
|
15
|
+
# links can never dangle (no more hand-authored, link-rotting indexes).
|
|
13
16
|
#
|
|
14
17
|
# Usage:
|
|
15
18
|
# okf-render-views.sh <BUNDLE_DIR> --arch-out <FILE> [--concept-map-into <FILE>]
|
|
@@ -25,6 +28,7 @@ BUNDLE=""
|
|
|
25
28
|
ARCH_OUT=""
|
|
26
29
|
WEB_OUT=""
|
|
27
30
|
CMAP_INTO=()
|
|
31
|
+
SECTION_INDEXES=0
|
|
28
32
|
COVERAGE_REPORT=""
|
|
29
33
|
VALIDATED_AT=""
|
|
30
34
|
|
|
@@ -39,6 +43,10 @@ Flags:
|
|
|
39
43
|
--arch-out FILE Write the rendered linear architecture.md here.
|
|
40
44
|
--concept-map-into FILE Inject the Concept Map between the CONCEPT-MAP markers
|
|
41
45
|
in FILE (repeatable: e.g. wiki/index.md and ai-context.md).
|
|
46
|
+
--section-indexes Regenerate each section's <section>/index.md concept
|
|
47
|
+
table (between its CONCEPT-MAP markers) from the pages
|
|
48
|
+
that actually exist in that directory. Eliminates
|
|
49
|
+
hand-authored, link-rotting section indexes.
|
|
42
50
|
--web FILE Write a self-contained, offline HTML viewer (single file:
|
|
43
51
|
all pages inlined, built-in markdown renderer, sidebar +
|
|
44
52
|
search). Double-click to open — no server, no internet.
|
|
@@ -56,6 +64,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
56
64
|
case "$1" in
|
|
57
65
|
--arch-out) ARCH_OUT="$2"; shift 2;;
|
|
58
66
|
--concept-map-into) CMAP_INTO+=("$2"); shift 2;;
|
|
67
|
+
--section-indexes) SECTION_INDEXES=1; shift;;
|
|
59
68
|
--web) WEB_OUT="$2"; shift 2;;
|
|
60
69
|
--coverage-report) COVERAGE_REPORT="$2"; shift 2;;
|
|
61
70
|
--validated-at) VALIDATED_AT="$2"; shift 2;;
|
|
@@ -100,6 +109,17 @@ strip_frontmatter() {
|
|
|
100
109
|
' "$1"
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
# Human-facing title for a page: frontmatter `title`, else the first `# H1` in the
|
|
113
|
+
# body, else the bundle-relative path. Index files are frontmatter-less under OKF
|
|
114
|
+
# §6, so the H1 fallback keeps the rendered TOC / nav readable.
|
|
115
|
+
page_title() {
|
|
116
|
+
local t; t="$(get_yaml_field "$1" title)"
|
|
117
|
+
if [[ -z "$t" ]]; then
|
|
118
|
+
t="$(strip_frontmatter "$1" | grep -m1 -E '^#[[:space:]]+' | sed -E 's/^#[[:space:]]+//')"
|
|
119
|
+
fi
|
|
120
|
+
printf '%s' "$t"
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
# Coverage-honesty banner, sourced from okf-coverage-check.sh's JSON report.
|
|
104
124
|
# Silent when no report is supplied (keeps the view backward-compatible).
|
|
105
125
|
emit_coverage_banner() {
|
|
@@ -134,7 +154,7 @@ render_architecture() {
|
|
|
134
154
|
echo ""
|
|
135
155
|
echo "# Architecture (Rendered View)"
|
|
136
156
|
echo ""
|
|
137
|
-
echo "> **Generated** from the \`wiki/\`
|
|
157
|
+
echo "> **Generated** from the \`wiki/\` bundle — do not edit by hand."
|
|
138
158
|
echo "> The bundle is the source of truth; this is the single-document linear"
|
|
139
159
|
echo "> view for onboarding. Regenerate with \`okf-render-views.sh\`."
|
|
140
160
|
echo ""
|
|
@@ -150,7 +170,7 @@ render_architecture() {
|
|
|
150
170
|
echo "- **${sec}/**"
|
|
151
171
|
last_sec="$sec"
|
|
152
172
|
fi
|
|
153
|
-
title="$(
|
|
173
|
+
title="$(page_title "$BUNDLE/$rel")"
|
|
154
174
|
[[ -n "$title" ]] || title="$rel"
|
|
155
175
|
local anchor; anchor="$(printf '%s' "$title" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-')"
|
|
156
176
|
anchor="${anchor#-}"; anchor="${anchor%-}"
|
|
@@ -174,10 +194,14 @@ render_architecture() {
|
|
|
174
194
|
build_concept_map() {
|
|
175
195
|
echo "| Concept | Type | Open it when… |"
|
|
176
196
|
echo "|---------|------|---------------|"
|
|
177
|
-
local rel type title desc
|
|
197
|
+
local rel base type title desc
|
|
178
198
|
while IFS= read -r -d '' page; do
|
|
179
199
|
rel="${page#"$BUNDLE/"}"
|
|
180
|
-
|
|
200
|
+
base="$(basename "$rel")"
|
|
201
|
+
# Meta pages are not routable concepts (reserved index/log + the typed,
|
|
202
|
+
# tool-generated coverage page).
|
|
203
|
+
[[ "$base" == "index.md" || "$base" == "log.md" || "$base" == "coverage.md" ]] && continue
|
|
204
|
+
grep -q '<!-- okf:coverage-generated -->' "$page" 2>/dev/null && continue
|
|
181
205
|
type="$(get_yaml_field "$page" type)"
|
|
182
206
|
[[ -n "$type" ]] || continue
|
|
183
207
|
title="$(get_yaml_field "$page" title)"
|
|
@@ -192,6 +216,50 @@ build_concept_map() {
|
|
|
192
216
|
done < <(find "$BUNDLE" -type f -name '*.md' -print0 | sort -z)
|
|
193
217
|
}
|
|
194
218
|
|
|
219
|
+
# First non-empty line of a page's `description` frontmatter (handles folded `>`).
|
|
220
|
+
page_desc() {
|
|
221
|
+
awk '
|
|
222
|
+
NR==1&&/^---$/{fm=1;next} fm&&/^---$/{exit}
|
|
223
|
+
fm && /^description:/ { collect=1; sub(/^description:[[:space:]]*>?[[:space:]]*/,""); if($0!=""){print; exit} next }
|
|
224
|
+
fm && collect { sub(/^[[:space:]]+/,""); if($0!=""){print; exit} }
|
|
225
|
+
' "$1"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# Build the per-section concept table (stdout) for a single section directory.
|
|
229
|
+
# Links are bundle-section-relative (just the filename) so they resolve from the
|
|
230
|
+
# section's own index.md. Only pages that actually exist are listed — so the
|
|
231
|
+
# table can never point at a missing file.
|
|
232
|
+
build_section_map() {
|
|
233
|
+
local dir="$1" f rel base type title desc
|
|
234
|
+
echo "| Concept | Type | Routing description |"
|
|
235
|
+
echo "|---------|------|---------------------|"
|
|
236
|
+
while IFS= read -r f; do
|
|
237
|
+
base="$(basename "$f")"
|
|
238
|
+
[[ "$base" == "index.md" ]] && continue
|
|
239
|
+
[[ "$base" == "coverage.md" ]] && continue
|
|
240
|
+
grep -q '<!-- okf:coverage-generated -->' "$f" 2>/dev/null && continue
|
|
241
|
+
type="$(get_yaml_field "$f" type)"
|
|
242
|
+
[[ -n "$type" ]] || continue
|
|
243
|
+
title="$(get_yaml_field "$f" title)"; [[ -n "$title" ]] || title="$base"
|
|
244
|
+
desc="$(page_desc "$f")"
|
|
245
|
+
echo "| [${title}](${base}) | ${type} | ${desc} |"
|
|
246
|
+
done < <(find "$dir" -maxdepth 1 -type f -name '*.md' | sort)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# Regenerate every <section>/index.md concept table from real pages.
|
|
250
|
+
render_section_indexes() {
|
|
251
|
+
local sec dir idx
|
|
252
|
+
for sec in "${SECTIONS[@]}"; do
|
|
253
|
+
dir="$BUNDLE/$sec"
|
|
254
|
+
idx="$dir/index.md"
|
|
255
|
+
[[ -d "$dir" && -f "$idx" ]] || continue
|
|
256
|
+
local map_tmp; map_tmp="$(mktemp)"
|
|
257
|
+
build_section_map "$dir" >"$map_tmp"
|
|
258
|
+
inject_concept_map "$idx" "$map_tmp"
|
|
259
|
+
rm -f "$map_tmp"
|
|
260
|
+
done
|
|
261
|
+
}
|
|
262
|
+
|
|
195
263
|
# Inject the Concept Map between markers in a target file (path may be relative
|
|
196
264
|
# to BUNDLE: links in the map are bundle-relative, so the target should resolve
|
|
197
265
|
# them — wiki/index.md works directly; an index root above wiki/ should prefix).
|
|
@@ -277,7 +345,7 @@ HTML_HEAD
|
|
|
277
345
|
while IFS= read -r -d '' page; do
|
|
278
346
|
local rel title type
|
|
279
347
|
rel="${page#"$BUNDLE/"}"
|
|
280
|
-
title="$(
|
|
348
|
+
title="$(page_title "$page")"; [[ -n "$title" ]] || title="$rel"
|
|
281
349
|
type="$(get_yaml_field "$page" type)"
|
|
282
350
|
printf '%s: {"title": %s, "type": %s, "md": %s},\n' \
|
|
283
351
|
"$(jq -Rn --arg v "$rel" '$v')" \
|
|
@@ -397,7 +465,10 @@ if [[ ${#CMAP_INTO[@]} -gt 0 ]]; then
|
|
|
397
465
|
rm -f "$MAP_TMP"
|
|
398
466
|
fi
|
|
399
467
|
|
|
468
|
+
[[ $SECTION_INDEXES -eq 1 ]] && render_section_indexes
|
|
469
|
+
|
|
400
470
|
[[ -n "$WEB_OUT" ]] && render_web "$WEB_OUT"
|
|
401
471
|
|
|
402
|
-
[[ -n "$ARCH_OUT" || -n "$WEB_OUT" || ${#CMAP_INTO[@]} -gt 0
|
|
472
|
+
[[ -n "$ARCH_OUT" || -n "$WEB_OUT" || ${#CMAP_INTO[@]} -gt 0 || $SECTION_INDEXES -eq 1 ]] \
|
|
473
|
+
|| { echo "ERROR: nothing to do (pass --arch-out, --web, --section-indexes, and/or --concept-map-into)" >&2; exit 1; }
|
|
403
474
|
exit 0
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
# Pages in scope: any *.md whose frontmatter declares a frozen `type:`. Section
|
|
12
12
|
# index.md pages, log.md, and the generated coverage page are excluded.
|
|
13
13
|
#
|
|
14
|
-
# Checks (per type): required H2 sections
|
|
14
|
+
# Checks (per type): required H2 sections AND real content beneath each (so a
|
|
15
|
+
# present-but-empty heading fails as "shallow"), min body lines, >=1 mermaid block
|
|
15
16
|
# (diagram types only), min x-grounded-paths, anti-stub patterns, unreplaced
|
|
16
17
|
# template tokens, duplicate "What it is" paragraphs, and a syntax-only mermaid
|
|
17
18
|
# lint (no Node, no headless browser).
|
|
@@ -88,6 +89,19 @@ grounded_count() {
|
|
|
88
89
|
|
|
89
90
|
has_section() { grep -qE "^##[[:space:]]+$1([[:space:]]|$)" "$2"; }
|
|
90
91
|
|
|
92
|
+
# Count non-blank content lines under a given H2 section (heading itself
|
|
93
|
+
# excluded), stopping at the next H2. Used to fail a section that is present as a
|
|
94
|
+
# heading but carries no real content — the classic "shallow" page.
|
|
95
|
+
section_content_lines() {
|
|
96
|
+
local sec="$1" file="$2"
|
|
97
|
+
awk -v sec="$sec" '
|
|
98
|
+
$0 ~ "^##[[:space:]]+" sec "([[:space:]]|$)" { grab=1; next }
|
|
99
|
+
grab && /^##[[:space:]]/ { exit }
|
|
100
|
+
grab && /[^[:space:]]/ { n++ }
|
|
101
|
+
END { print n+0 }
|
|
102
|
+
' "$file"
|
|
103
|
+
}
|
|
104
|
+
|
|
91
105
|
mermaid_block_count() { grep -cE '^[[:space:]]*```mermaid' "$1" || true; }
|
|
92
106
|
|
|
93
107
|
# Syntax-only mermaid lint: catch the breakers that silently fail previewers
|
|
@@ -139,19 +153,22 @@ whatitis_hash() {
|
|
|
139
153
|
ANTI_STUB='see architecture\.md|deferred to ref-docs|\bTBD\b|TODO:[[:space:]]*document|stub page|placeholder page'
|
|
140
154
|
TOKEN_RE='\{[A-Z_]+\}'
|
|
141
155
|
|
|
142
|
-
# Per-type policy. echoes: sections|min_lines|need_mermaid|min_grounded
|
|
156
|
+
# Per-type policy. echoes: sections|min_lines|need_mermaid|min_grounded|min_section_lines
|
|
157
|
+
# min_section_lines: minimum non-blank content lines required UNDER each named
|
|
158
|
+
# section (0 disables the per-section depth check). This is what turns a
|
|
159
|
+
# present-but-empty heading — the hallmark of a shallow page — into a failure.
|
|
143
160
|
type_policy() {
|
|
144
161
|
case "$1" in
|
|
145
162
|
Subsystem|Module|Feature|Entrypoint)
|
|
146
|
-
echo "What it is;How it works;Used by;Blast radius;See also|
|
|
163
|
+
echo "What it is;How it works;Used by;Blast radius;See also|35|1|2|2";;
|
|
147
164
|
API|DataModel)
|
|
148
|
-
echo "What it is;How it works;See also|
|
|
165
|
+
echo "What it is;How it works;See also|22|0|1|2";;
|
|
149
166
|
Dependency)
|
|
150
|
-
echo "What it is;Used by|
|
|
167
|
+
echo "What it is;Used by|12|0|0|1";;
|
|
151
168
|
ADR|Runbook)
|
|
152
|
-
echo "|8|0|0";;
|
|
169
|
+
echo "|8|0|0|0";;
|
|
153
170
|
*)
|
|
154
|
-
echo "|8|0|0";;
|
|
171
|
+
echo "|8|0|0|0";;
|
|
155
172
|
esac
|
|
156
173
|
}
|
|
157
174
|
|
|
@@ -172,13 +189,26 @@ while IFS= read -r -d '' page; do
|
|
|
172
189
|
[[ -z "$type_val" ]] && continue # not a concept page
|
|
173
190
|
CHECKED=$((CHECKED + 1))
|
|
174
191
|
|
|
175
|
-
IFS='|' read -r sections min_lines need_mermaid min_grounded <<< "$(type_policy "$type_val")"
|
|
192
|
+
IFS='|' read -r sections min_lines need_mermaid min_grounded min_section_lines <<< "$(type_policy "$type_val")"
|
|
176
193
|
|
|
177
|
-
# Q-SEC: required sections.
|
|
194
|
+
# Q-SEC: required sections must be present. Q-SECLEN: the NARRATIVE sections
|
|
195
|
+
# ("What it is" / "How it works") must carry real prose beneath the heading —
|
|
196
|
+
# a heading with nothing under it is the classic shallow stub. List sections
|
|
197
|
+
# ("Used by", "See also", "Blast radius") are legitimately terse, so the
|
|
198
|
+
# depth bar only applies to the narrative ones.
|
|
178
199
|
if [[ -n "$sections" ]]; then
|
|
179
200
|
IFS=';' read -ra secs <<< "$sections"
|
|
180
201
|
for s in "${secs[@]}"; do
|
|
181
|
-
has_section "$s" "$page"
|
|
202
|
+
if ! has_section "$s" "$page"; then
|
|
203
|
+
fail "$rel" "Q-SEC" "missing required section '## $s'"
|
|
204
|
+
elif [[ "${min_section_lines:-0}" -gt 0 ]]; then
|
|
205
|
+
case "$s" in
|
|
206
|
+
"What it is"|"How it works")
|
|
207
|
+
scl="$(section_content_lines "$s" "$page")"
|
|
208
|
+
[[ "$scl" -ge "$min_section_lines" ]] \
|
|
209
|
+
|| fail "$rel" "Q-SECLEN" "section '## $s' has $scl content lines < $min_section_lines (shallow)";;
|
|
210
|
+
esac
|
|
211
|
+
fi
|
|
182
212
|
done
|
|
183
213
|
fi
|
|
184
214
|
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
# 2. Every concept page (any *.md whose frontmatter declares `type:`) carries
|
|
12
12
|
# all required OKF frontmatter keys: type, title, description, resource.
|
|
13
13
|
# 3. Every declared `type` is in the frozen code-repo vocabulary (§4 of HLD).
|
|
14
|
+
# 3a. Every non-meta *.md is a real concept: an empty or frontmatter-less page
|
|
15
|
+
# (a blank placeholder) fails — it would otherwise be invisible to the
|
|
16
|
+
# quality + coverage layers, which both key on `type`.
|
|
17
|
+
# 3b. No page carries an unreplaced {ALL_CAPS} template token (catches leftover
|
|
18
|
+
# placeholders in hand-seeded index pages, which the quality layer skips).
|
|
14
19
|
# 4. Every relative markdown cross-link ( ](path.md) ) resolves to a file that
|
|
15
20
|
# exists inside the bundle. External (http/https/mailto) and pure-anchor
|
|
16
21
|
# (#frag) links are ignored.
|
|
@@ -105,14 +110,53 @@ if [[ ! -f "$BUNDLE/index.md" ]]; then
|
|
|
105
110
|
add_error "missing bundle root: $BUNDLE/index.md"
|
|
106
111
|
fi
|
|
107
112
|
|
|
113
|
+
# A page is "meta" (not a concept) if it is a section/root index, the change log,
|
|
114
|
+
# or the tool-generated coverage page. Everything else MUST be a real concept —
|
|
115
|
+
# an empty or frontmatter-less .md placeholder is a completeness failure, not an
|
|
116
|
+
# invisible non-concept.
|
|
117
|
+
is_meta_page() {
|
|
118
|
+
local base="$1" page="$2"
|
|
119
|
+
case "$base" in
|
|
120
|
+
index.md|log.md|coverage.md) return 0;;
|
|
121
|
+
esac
|
|
122
|
+
grep -q '<!-- okf:coverage-generated -->' "$page" 2>/dev/null && return 0
|
|
123
|
+
return 1
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Non-blank body line count (everything after the frontmatter block).
|
|
127
|
+
nonblank_body_lines() {
|
|
128
|
+
awk 'NR==1&&/^---$/{fm=1;next} fm&&/^---$/{fm=0;next} !fm{print}' "$1" \
|
|
129
|
+
| grep -cE '[^[:space:]]' || true
|
|
130
|
+
}
|
|
131
|
+
|
|
108
132
|
# --- 2/3. Per-page frontmatter + type vocabulary ---
|
|
109
133
|
while IFS= read -r -d '' page; do
|
|
110
134
|
PAGE_COUNT=$((PAGE_COUNT + 1))
|
|
111
135
|
rel="${page#"$BUNDLE/"}"
|
|
136
|
+
base="$(basename "$rel")"
|
|
137
|
+
|
|
138
|
+
# Meta pages are NOT concepts: reserved index.md/log.md (OKF §6/§7) carry no
|
|
139
|
+
# concept frontmatter, and the tool-generated coverage page uses a descriptive
|
|
140
|
+
# (non-frozen) type. Exempt them from the frozen-vocab + required-field + body
|
|
141
|
+
# checks, and don't count them as concepts. The template-token (§3b) and
|
|
142
|
+
# cross-link (§4) scans below still cover them.
|
|
143
|
+
if is_meta_page "$base" "$page"; then
|
|
144
|
+
continue
|
|
145
|
+
fi
|
|
112
146
|
|
|
113
|
-
# A page is a "concept" only if its frontmatter declares a type.
|
|
114
147
|
type_val="$(get_yaml_field "$page" "type")"
|
|
115
|
-
|
|
148
|
+
|
|
149
|
+
# Empty / placeholder pages: a non-meta page with no type (or no body) slips
|
|
150
|
+
# past every downstream check (quality + coverage both key on `type`). Catch
|
|
151
|
+
# it here so a blank or stub file can never ship.
|
|
152
|
+
if [[ -z "$type_val" ]]; then
|
|
153
|
+
if [[ "$(nonblank_body_lines "$page")" -eq 0 ]]; then
|
|
154
|
+
add_error "$rel: empty page (no frontmatter type, no body) — every wiki page must be a real concept"
|
|
155
|
+
else
|
|
156
|
+
add_error "$rel: untyped page (missing frontmatter 'type:') — not a valid concept page"
|
|
157
|
+
fi
|
|
158
|
+
continue
|
|
159
|
+
fi
|
|
116
160
|
CONCEPT_COUNT=$((CONCEPT_COUNT + 1))
|
|
117
161
|
|
|
118
162
|
for key in title description resource; do
|
|
@@ -124,6 +168,24 @@ while IFS= read -r -d '' page; do
|
|
|
124
168
|
if ! is_known_type "$type_val"; then
|
|
125
169
|
add_error "$rel: unknown concept type '$type_val' (frozen vocab: $OKF_TYPES)"
|
|
126
170
|
fi
|
|
171
|
+
|
|
172
|
+
# A typed concept page with no body is still a stub — fail it even though the
|
|
173
|
+
# depth/anti-stub bars live in the quality layer (this layer must stand alone).
|
|
174
|
+
if [[ "$(nonblank_body_lines "$page")" -eq 0 ]]; then
|
|
175
|
+
add_error "$rel: concept page has empty body"
|
|
176
|
+
fi
|
|
177
|
+
done < <(find "$BUNDLE" -type f -name '*.md' -print0 | sort -z)
|
|
178
|
+
|
|
179
|
+
# --- 3b. Unreplaced template tokens (any page, including index pages) ---
|
|
180
|
+
# Quality checks skip index.md pages, so a leftover {SECTION_TITLE}/{PROJECT_NAME}
|
|
181
|
+
# placeholder in a hand-seeded index would otherwise survive. Match {ALL_CAPS}
|
|
182
|
+
# tokens (safe: real prose almost never contains them).
|
|
183
|
+
while IFS= read -r -d '' page; do
|
|
184
|
+
prel="${page#"$BUNDLE/"}"
|
|
185
|
+
if grep -qE '\{[A-Z][A-Z0-9_]+\}' "$page" 2>/dev/null; then
|
|
186
|
+
tok="$(grep -oE '\{[A-Z][A-Z0-9_]+\}' "$page" | head -1)"
|
|
187
|
+
add_error "$prel: unreplaced template token '$tok'"
|
|
188
|
+
fi
|
|
127
189
|
done < <(find "$BUNDLE" -type f -name '*.md' -print0 | sort -z)
|
|
128
190
|
|
|
129
191
|
# --- 4. Cross-link resolution ---
|
package/skills/init/SKILL.md
CHANGED
|
@@ -221,7 +221,7 @@ The tier-gated default rests on **maintainability/readability** (one navigable c
|
|
|
221
221
|
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).
|
|
222
222
|
2. Every `required` entry in `concept-plan.json` has a non-stub page.
|
|
223
223
|
3. `okf-validate-all.sh … --plan … --strict` exits 0 (structure + per-type quality + coverage all pass).
|
|
224
|
-
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) —
|
|
224
|
+
4. `systems/coverage.md` was generated by tooling (verify the `<!-- okf:coverage-generated -->` marker) — **every** package/module the graph found is required by default (the fan-in floor only types Subsystem-vs-Module, it does not exempt), so none may be **MISSING**. Section `index.md` tables are regenerated by `okf-render-views.sh --section-indexes`, never hand-authored, so their links cannot dangle.
|
|
225
225
|
5. On any failure: **do not** atomic-rename; surface `.state/validation-report.json`.
|
|
226
226
|
|
|
227
227
|
> **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.
|
|
@@ -77,7 +77,8 @@ Templates for each bundle page live in `core/templates/okf/` (`index.md`,
|
|
|
77
77
|
## Frozen `type` vocabulary
|
|
78
78
|
|
|
79
79
|
Every concept carries a `type` from this frozen set (changing it churns every
|
|
80
|
-
file; versioned via `
|
|
80
|
+
file; versioned via the `okf-types-version` comment in the wiki root `index.md`
|
|
81
|
+
body — OKF §6/§11 permit only `okf_version` in an index file's frontmatter):
|
|
81
82
|
|
|
82
83
|
| type | Maps to | Home |
|
|
83
84
|
|------|---------|------|
|
|
@@ -103,6 +104,16 @@ the task at hand?"), never a summary. Draft extensions are namespaced `x-` and
|
|
|
103
104
|
ignored by generic OKF consumers: `x-grounded-paths`, `x-hotspot-score`,
|
|
104
105
|
`x-callers`.
|
|
105
106
|
|
|
107
|
+
**Meta pages are not concepts.** Index files (`wiki/index.md` and every
|
|
108
|
+
`<section>/index.md`) carry **no concept frontmatter**: per OKF §6 an index file
|
|
109
|
+
has no frontmatter, and per §11 the root `index.md` may declare **only**
|
|
110
|
+
`okf_version`. The tool-generated `systems/coverage.md` is a non-reserved file, so
|
|
111
|
+
OKF §9.1/§9.2 require it to carry a typed frontmatter block; it uses a descriptive
|
|
112
|
+
`type: Report` and is exempt from the frozen vocabulary via `is_meta_page`
|
|
113
|
+
(basename + the `<!-- okf:coverage-generated -->` marker). `okf-validate.sh`
|
|
114
|
+
short-circuits all meta pages before the concept checks, so they are never
|
|
115
|
+
vocab-checked or counted as concepts.
|
|
116
|
+
|
|
106
117
|
## Concept granularity (resolves open decision 1)
|
|
107
118
|
|
|
108
119
|
Derive concepts from the graph, not by hand:
|
|
@@ -122,17 +133,21 @@ Derive concepts from the graph, not by hand:
|
|
|
122
133
|
```
|
|
123
134
|
1. Survey → existing /draft:init 5-phase + graph snapshot (graph-snapshot.sh)
|
|
124
135
|
2. Plan → DETERMINISTIC. okf-plan-concepts.sh derives the expected-concept
|
|
125
|
-
set from the graph
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
set from the graph. EVERY package the graph knows about is
|
|
137
|
+
required (fan_in ≥ floor → Subsystem; below floor → Module — the
|
|
138
|
+
floor only types/orders, it never exempts); entrypoints → required;
|
|
139
|
+
only --allow-defer matches are deferred (with a reason). Writes
|
|
140
|
+
draft.tmp/.state/concept-plan.json.
|
|
128
141
|
okf-plan-concepts.sh --repo . [--scope PATH] \
|
|
129
142
|
[--manifest FILE] [--min-fan-in 2] [--allow-defer GLOB]... \
|
|
130
143
|
--out draft.tmp/.state/concept-plan.json
|
|
131
144
|
This replaces the old in-context concept enumeration — the boundary
|
|
132
145
|
of the work is now a tool output, not an LLM judgment, so modules
|
|
133
|
-
cannot be silently dropped.
|
|
134
|
-
|
|
135
|
-
(required
|
|
146
|
+
and sub-modules cannot be silently dropped. (Legacy fan-in
|
|
147
|
+
exemption is opt-in via --defer-below-floor.) LOG the counts
|
|
148
|
+
(expected/required/deferred) BEFORE writing any page.
|
|
149
|
+
`generated_order` is topo-ish (required + high-fan-in first) so
|
|
150
|
+
forward cross-links resolve.
|
|
136
151
|
3. Generate → iterate concept-plan.generated_order; write ONE page per REQUIRED
|
|
137
152
|
entry, grounding each from the graph:
|
|
138
153
|
x-callers ← graph-callers.sh --symbol <c>
|
|
@@ -152,9 +167,11 @@ Derive concepts from the graph, not by hand:
|
|
|
152
167
|
--plan draft.tmp/.state/concept-plan.json \
|
|
153
168
|
--path-index draft.tmp/.state/path-to-concept.json \
|
|
154
169
|
--strict --report draft.tmp/.state/validation-report.json
|
|
155
|
-
It runs, in order: okf-validate.sh (structure + reverse index
|
|
156
|
-
|
|
157
|
-
okf-
|
|
170
|
+
It runs, in order: okf-validate.sh (structure + reverse index +
|
|
171
|
+
empty/untyped-page + leftover-template-token + dangling-link checks),
|
|
172
|
+
okf-validate-quality.sh (per-type anti-stub / depth / per-section
|
|
173
|
+
content / mermaid lint), okf-coverage-check.sh (every required plan
|
|
174
|
+
entry → real page).
|
|
158
175
|
ANY layer failing ⇒ exit non-zero ⇒ DO NOT atomic-rename.
|
|
159
176
|
coverage.md (systems/coverage.md) is regenerated by the coverage
|
|
160
177
|
layer; it is tool-owned (marker <!-- okf:coverage-generated -->) —
|
|
@@ -193,6 +210,7 @@ regenerated on every init/refresh so they never drift from the bundle:
|
|
|
193
210
|
```bash
|
|
194
211
|
okf-render-views.sh draft/wiki \
|
|
195
212
|
--arch-out draft/architecture.md \
|
|
213
|
+
--section-indexes \
|
|
196
214
|
--concept-map-into draft/wiki/index.md \
|
|
197
215
|
--concept-map-into draft/.ai-context.md \
|
|
198
216
|
--web draft/wiki/web/index.html
|
|
@@ -200,6 +218,11 @@ okf-render-views.sh draft/wiki \
|
|
|
200
218
|
|
|
201
219
|
- `--arch-out` renders the linear `architecture.md` (banner + TOC + every concept
|
|
202
220
|
page in canonical section order, frontmatter stripped, Mermaid preserved).
|
|
221
|
+
- `--section-indexes` rebuilds each `<section>/index.md` concept table (between its
|
|
222
|
+
`CONCEPT-MAP` markers) from the pages that actually exist in that directory. This
|
|
223
|
+
is mandatory: section indexes are NOT hand-authored — building them from real
|
|
224
|
+
files is what makes their links impossible to dangle. Never write a section
|
|
225
|
+
index "Concepts" table by hand.
|
|
203
226
|
- `--concept-map-into` rebuilds the routing table between the
|
|
204
227
|
`<!-- CONCEPT-MAP:START -->` / `:END` markers from each concept's `title` +
|
|
205
228
|
`type` + `description` (section `index.md` pages excluded).
|