akm-cli 0.9.30 → 0.9.31

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.
Files changed (161) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/dist/commands/improve/improve-cli.js +33 -12
  5. package/dist/core/file-lock.js +10 -1
  6. package/dist/llm/client.js +33 -3
  7. package/dist/scripts/akm-migrate-node.js +5 -1
  8. package/dist/scripts/akm-migrate.js +5 -1
  9. package/docs/README.md +42 -41
  10. package/docs/agents/README.md +15 -0
  11. package/docs/agents/agent-install.md +14 -0
  12. package/docs/architecture/README.md +67 -0
  13. package/docs/architecture/adapters.md +272 -0
  14. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  15. package/docs/architecture/akm-core-principles.md +126 -0
  16. package/docs/architecture/architecture.md +579 -0
  17. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  18. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  19. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  20. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  21. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  22. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  23. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  24. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  25. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  26. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  27. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  28. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  29. package/docs/architecture/decisions/README.md +62 -0
  30. package/docs/architecture/improvement.md +440 -0
  31. package/docs/architecture/internals/classification.md +154 -0
  32. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  33. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  34. package/docs/architecture/internals/health-advisories.md +110 -0
  35. package/docs/architecture/internals/improve-workflow.md +713 -0
  36. package/docs/architecture/internals/indexing.md +498 -0
  37. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  38. package/docs/architecture/internals/search.md +249 -0
  39. package/docs/architecture/internals/storage-locations.md +943 -0
  40. package/docs/architecture/persisted-data-compat.md +59 -0
  41. package/docs/architecture/reviews/env-secret-access.md +319 -0
  42. package/docs/architecture/runtime-boundary-design.md +153 -0
  43. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  44. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  45. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  46. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  47. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  48. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  49. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  50. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  51. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  52. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  53. package/docs/architecture/specs/di-seams-plan.md +539 -0
  54. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  55. package/docs/architecture/specs/fact-asset-type.md +142 -0
  56. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  57. package/docs/architecture/specs/okf-support.md +148 -0
  58. package/docs/architecture/specs/ref.md +329 -0
  59. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  60. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  61. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  73. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  74. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  75. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  76. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  77. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  78. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  79. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  80. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  81. package/docs/architecture/testing/testing-workflow.md +446 -0
  82. package/docs/architecture/workflow-engine.md +274 -0
  83. package/docs/guides/README.md +33 -0
  84. package/docs/guides/author-bundles.md +310 -0
  85. package/docs/guides/author-workflows.md +520 -0
  86. package/docs/guides/bundles.md +321 -0
  87. package/docs/guides/capture-knowledge.md +136 -0
  88. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  89. package/docs/guides/concepts.md +93 -0
  90. package/docs/guides/discover-and-load.md +158 -0
  91. package/docs/guides/getting-started.md +240 -0
  92. package/docs/guides/improve-the-library.md +246 -0
  93. package/docs/guides/knowledge-management.md +18 -0
  94. package/docs/guides/recipes/headless-install.md +97 -0
  95. package/docs/guides/recipes/website-source.md +28 -0
  96. package/docs/guides/run-workflows.md +279 -0
  97. package/docs/guides/scheduling.md +123 -0
  98. package/docs/guides/use-with-any-agent.md +129 -0
  99. package/docs/guides/wikis.md +81 -0
  100. package/docs/maintainers/README.md +19 -0
  101. package/docs/maintainers/curate-workmap.md +336 -0
  102. package/docs/maintainers/eval.md +3 -0
  103. package/docs/maintainers/local-development.md +108 -0
  104. package/docs/maintainers/release-checklist.md +103 -0
  105. package/docs/migration/README.md +2 -2
  106. package/docs/migration/release-notes/0.0.13.md +1 -1
  107. package/docs/migration/release-notes/0.1.0.md +1 -1
  108. package/docs/migration/release-notes/0.2.0.md +1 -1
  109. package/docs/migration/release-notes/0.3.0.md +1 -1
  110. package/docs/migration/release-notes/0.5.0.md +1 -1
  111. package/docs/migration/release-notes/0.6.0.md +1 -1
  112. package/docs/migration/release-notes/0.7.0.md +1 -1
  113. package/docs/migration/release-notes/0.7.3.md +2 -2
  114. package/docs/migration/release-notes/0.7.4.md +1 -1
  115. package/docs/migration/release-notes/0.7.5.md +1 -1
  116. package/docs/migration/release-notes/0.8.0.md +1 -1
  117. package/docs/migration/release-notes/0.9.0.md +1 -1
  118. package/docs/migration/release-notes/0.9.14.md +1 -1
  119. package/docs/migration/release-notes/0.9.15.md +1 -1
  120. package/docs/migration/release-notes/0.9.16.md +1 -1
  121. package/docs/migration/release-notes/0.9.17.md +1 -1
  122. package/docs/migration/release-notes/0.9.19.md +1 -1
  123. package/docs/migration/release-notes/0.9.2.md +1 -1
  124. package/docs/migration/release-notes/README.md +27 -31
  125. package/docs/migration/v0.5-to-v0.6.md +759 -0
  126. package/docs/migration/v0.8-to-v0.9.md +1 -1
  127. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  128. package/docs/posts/README.md +30 -0
  129. package/docs/posts/akm-command-tour.md +495 -0
  130. package/docs/posts/akm-logo-sized.webp +0 -0
  131. package/docs/posts/belief-aware-memory-12.md +139 -0
  132. package/docs/posts/config-v2-lessons-16.md +191 -0
  133. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  134. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  135. package/docs/posts/intro-part-01.md +76 -0
  136. package/docs/posts/intro-part-02.md +221 -0
  137. package/docs/posts/intro-part-03.md +190 -0
  138. package/docs/posts/intro-part-04.md +221 -0
  139. package/docs/posts/llm-mode-reflect-13.md +166 -0
  140. package/docs/posts/openpalm-built-on-akm.md +86 -0
  141. package/docs/posts/progressive-disclosure-06.md +129 -0
  142. package/docs/posts/proposal-safety-net-15.md +112 -0
  143. package/docs/posts/release-0.5.0.md +247 -0
  144. package/docs/posts/release-0.6.0.md +117 -0
  145. package/docs/posts/release-0.7.0.md +159 -0
  146. package/docs/posts/release-0.8.0.md +332 -0
  147. package/docs/posts/scattered-skills-05.md +100 -0
  148. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  149. package/docs/posts/team-skills-07.md +198 -0
  150. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  151. package/docs/posts/workflows-vaults-09.md +321 -0
  152. package/docs/product-surface.md +54 -0
  153. package/docs/reference/README.md +1 -2
  154. package/docs/reference/asset-types.md +168 -0
  155. package/docs/reference/cli.md +5 -6
  156. package/docs/reference/env-and-secrets.md +126 -0
  157. package/docs/reference/memory.md +124 -0
  158. package/docs/reference/refs.md +156 -0
  159. package/docs/reference/registry.md +489 -0
  160. package/docs/reference/website-sources.md +141 -0
  161. package/package.json +12 -15
@@ -0,0 +1,924 @@
1
+ # OKF v0.2 Conformance Re-Evaluation Runbook
2
+
3
+ Use this runbook after changing AKM's adapters, indexing, refs, writes, or lint
4
+ behavior. It is the acceptance procedure for
5
+ [first-class OKF format support](../specs/okf-support.md). It runs without
6
+ reading or modifying the host's AKM installation, bundle, configuration, cache,
7
+ or index.
8
+
9
+ The runbook deliberately tests observable behavior in addition to unit tests.
10
+ Passing adapter-local tests is not enough: the original failures happened
11
+ between recognition, persistence, search output, and command ref parsing.
12
+
13
+ **v0.2 update (#730):** upstream OKF minor-versioned to v0.2 (trust/provenance
14
+ family `generated`/`verified`/`sources`; lifecycle family `status`/
15
+ `stale_after`; an actor convention; `timestamp` superseded by `generated.at`
16
+ with a permitted fallback). Rules 1-9, Producer, and Lint below are unchanged
17
+ and still gate v0.1-only bundles; Rules 10-14 are new v0.2-specific gates. The
18
+ `okf` adapter stays **consumer-only** for both spec versions — see
19
+ `okf-support.md`'s v0.2 note for why AKM's own provenance *writes* go through
20
+ the proposal path onto AKM-native assets instead, and are out of this
21
+ runbook's scope (which evaluates the `okf` adapter's read/consumer behavior
22
+ against third-party and reference OKF bundles).
23
+
24
+ ## Success Criteria
25
+
26
+ The evaluation passes only when all applicable statements are true:
27
+
28
+ | Rule | Required result |
29
+ |---|---|
30
+ | 1 | Every conformant non-reserved Markdown concept is represented by its path-minus-`.md` identity. Different paths cannot collide even when their titles match. Search emits that identity, and `show` accepts it. |
31
+ | 2 | Root and nested `index.md`/`log.md` files never become concepts. |
32
+ | 3 | Any non-empty `type` value survives unchanged and receives generic behavior when unknown. |
33
+ | 4 | Missing title falls back to the filename without changing identity. |
34
+ | 5 | If AKM modifies an existing OKF document, unknown nested frontmatter keys and the body survive unless the requested operation explicitly changes them. If AKM has no OKF write path, record this rule as not applicable rather than satisfied. |
35
+ | 6 | Inline, root-relative, relative, reference-style, and dangling concept links survive as durable directed relationships. A dangling target does not reject indexing. |
36
+ | 7 | Index synthesis is optional. Record whether it exists; do not fail solely because it does not. |
37
+ | 8 | Missing optional fields, unknown type, unknown keys, dangling links, and missing index files do not cause OKF semantics to be abandoned. |
38
+ | 9 | An unknown `okf_version` receives best-effort consumption. |
39
+ | 10 | `generated.at`, when present, takes precedence over legacy `timestamp` for the freshness/`updated` field; `timestamp` alone remains a fully valid fallback reading; neither present is not an AKM freshness error. |
40
+ | 11 | `verified` is read in both its list form and its v0.2-permitted single-mapping (no list dash) form; each entry survives as a durable `{by, at?}` pair; a malformed entry is dropped, never rejecting the document. |
41
+ | 12 | `sources` as a v0.2 object list (`resource` required; `id`/`title`/`author`/`usage_count`/`last_modified` optional) survives intact and distinctly from any pre-existing AKM `sources` convention — it must never be silently coerced into a bare string list (which would drop every entry). |
42
+ | 13 | `status` (`draft`/`stable`/`deprecated`) and `stale_after` are read verbatim when present; an unrecognized `status` value is ignored rather than guessed at, and neither field's absence is an error. |
43
+ | 14 | If AKM's own accepted-proposal writes stamp OKF v0.2 provenance onto AKM-native assets, record what is stamped and confirm it never applies to an OKF-adapter target (Rule 5 / runbook §10's write-rejection contract must stay intact). If AKM has no such write path, record this rule as not applicable. |
44
+ | Producer | If AKM claims to produce OKF, every emitted non-reserved Markdown file has parseable mapping frontmatter with a non-empty type, and reserved files have structural content. Otherwise record AKM as consumer-only. |
45
+ | Lint | `--fail-on-flagged` does not turn a dangling OKF body link into a failing exit status. |
46
+
47
+ Type participation in FTS and ranking is not a conformance gate. Record it for
48
+ regression awareness, but do not fail the OKF verdict because of it.
49
+
50
+ ## 1. Prerequisites And Subject Capture
51
+
52
+ Run from the AKM repository root. Required host tools are Docker and Git. Bun,
53
+ Node, AKM, SQLite, and the Google checkout run only inside Docker.
54
+
55
+ Record the exact subject before testing:
56
+
57
+ ```bash
58
+ git status --short
59
+ git rev-parse HEAD
60
+ git diff --stat
61
+ ```
62
+
63
+ Uncommitted source changes are included in the Docker build because the local
64
+ working tree is the build context. Existing `node_modules`, `dist`, `.git`, and
65
+ developer state are excluded by `.dockerignore`.
66
+
67
+ Set stable names and pin the same upstream OKF snapshot used by the baseline
68
+ audit:
69
+
70
+ ```bash
71
+ export AKM_OKF_IMAGE="akm-okf-eval:local"
72
+ export AKM_OKF_CONTAINER="akm-okf-eval"
73
+ export KC_REF="3fcbb9f828c2f23d109c855ee403c3a4c81f3a96"
74
+ export AKM_SUBJECT="$(git rev-parse HEAD)$(test -n "$(git status --porcelain)" && printf '%s' '-dirty')"
75
+ ```
76
+
77
+ If intentionally updating the OKF reference revision, inspect the upstream
78
+ `okf/SPEC.md` diff first. Do not silently compare AKM against a changed spec.
79
+
80
+ **2026-07-30 bump (#730):** `KC_REF` was moved from `d44368c15e38e7c92481c5992e4f9b5b421a801d`
81
+ (the v0.1 baseline) to `3fcbb9f828c2f23d109c855ee403c3a4c81f3a96`, obtained via
82
+ `git ls-remote https://github.com/GoogleCloudPlatform/knowledge-catalog.git HEAD`
83
+ and cross-checked against `git ls-remote ... refs/heads/main` (both resolved to
84
+ the same commit) — a real, verified upstream commit, not an invented one. The
85
+ `okf/SPEC.md` content at that exact commit was fetched and confirmed to state
86
+ "This document specifies OKF version 0.2" with the `generated`/`verified`
87
+ field definitions this update implements against. This runbook update was done
88
+ without Docker access to actually run the container-based evaluation in
89
+ this session; §§2-13 below are updated to describe the v0.2 procedure a
90
+ maintainer should run, but have not been re-executed end-to-end against a
91
+ live container since this bump. Treat the `KC_REF` bump and the procedural
92
+ updates as reviewed-but-unexecuted until a maintainer runs this runbook for
93
+ real.
94
+
95
+ ## 2. Build The Isolated AKM Image
96
+
97
+ This image contains the working tree under test, Bun, Git, and SQLite. It does
98
+ not mount the host home directory.
99
+
100
+ ```bash
101
+ docker build \
102
+ --build-arg AKM_SUBJECT="$AKM_SUBJECT" \
103
+ --tag "$AKM_OKF_IMAGE" \
104
+ --file - . <<'DOCKERFILE'
105
+ FROM oven/bun:1.3.6-debian
106
+ ARG AKM_SUBJECT
107
+ LABEL org.opencontainers.image.revision="$AKM_SUBJECT"
108
+ RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates sqlite3 && rm -rf /var/lib/apt/lists/*
109
+ WORKDIR /app
110
+ COPY . .
111
+ RUN bun install --frozen-lockfile
112
+ RUN bun run build
113
+ ENV PATH="/app/dist:${PATH}"
114
+ CMD ["sleep", "infinity"]
115
+ DOCKERFILE
116
+ ```
117
+
118
+ Start a long-lived evaluation container with an isolated home:
119
+
120
+ ```bash
121
+ docker rm -f "$AKM_OKF_CONTAINER" 2>/dev/null || true
122
+ docker run --detach \
123
+ --name "$AKM_OKF_CONTAINER" \
124
+ --env HOME=/tmp/akm-home \
125
+ --env NO_COLOR=1 \
126
+ "$AKM_OKF_IMAGE"
127
+ docker exec "$AKM_OKF_CONTAINER" mkdir -p /tmp/akm-home
128
+ docker exec "$AKM_OKF_CONTAINER" akm --version
129
+ ```
130
+
131
+ Pass condition: the reported version is the code under test, and no command
132
+ references the host's home or XDG directories.
133
+
134
+ ## 3. Clone And Pin Google's OKF Repository
135
+
136
+ ```bash
137
+ docker exec "$AKM_OKF_CONTAINER" git clone \
138
+ https://github.com/GoogleCloudPlatform/knowledge-catalog.git /tmp/kc
139
+ docker exec "$AKM_OKF_CONTAINER" git -C /tmp/kc checkout "$KC_REF"
140
+ docker exec "$AKM_OKF_CONTAINER" git -C /tmp/kc rev-parse HEAD
141
+ docker exec "$AKM_OKF_CONTAINER" sh -lc 'nl -ba /tmp/kc/okf/SPEC.md'
142
+ ```
143
+
144
+ Pass condition: the printed revision equals `KC_REF`. Retain the numbered spec
145
+ output with the evaluation record.
146
+
147
+ ## 4. Run Focused Existing Tests
148
+
149
+ Run the current adapter, registry, type, dispatch, and reserved-name tests:
150
+
151
+ ```bash
152
+ docker exec "$AKM_OKF_CONTAINER" bun test \
153
+ tests/integration/okf-conformance.test.ts \
154
+ tests/core/akm-markdown.test.ts \
155
+ tests/core/adapter/okf-adapter.test.ts \
156
+ tests/core/adapter/conformance.test.ts \
157
+ tests/core/adapter/registry.test.ts \
158
+ tests/indexer/installations.test.ts \
159
+ tests/core/type-token-contract.test.ts \
160
+ tests/integration/indexer/adapter-dispatch.test.ts \
161
+ tests/integration/reserved-filename-conformance.test.ts
162
+ ```
163
+
164
+ v0.2 update (#730): also run the promotion-path provenance-stamping coverage
165
+ (D2 — write side, AKM-native assets only, not the `okf` adapter itself) and
166
+ the OKF format-family golden:
167
+
168
+ ```bash
169
+ docker exec "$AKM_OKF_CONTAINER" bun test \
170
+ tests/integration/proposals.test.ts \
171
+ tests/core/adapter/okf-adapter.test.ts
172
+ ```
173
+
174
+ After implementing fixes, the repository should also contain focused
175
+ end-to-end tests for these behaviors:
176
+
177
+ | Coverage to require | Minimum assertion |
178
+ |---|---|
179
+ | Official OKF fixture | Persisted concept count equals all non-reserved `.md` files. |
180
+ | Identity | Same type/title at two paths produces two refs and two rows. |
181
+ | Search/show | Search ref is `<bundle>//<path-minus-.md>` and `show` accepts it. |
182
+ | Missing index | Explicitly configured or otherwise recognized OKF still uses OKF semantics. |
183
+ | v0.2 `generated`/`timestamp` | `generated.at` wins when both are present; `timestamp` alone still satisfies freshness; neither present is not an error. |
184
+ | v0.2 `verified` | Both the list form and the single-mapping form persist as a non-empty `{by, at?}` array. |
185
+ | v0.2 `sources` | An object list survives intact and distinctly from the bare AKM `sources: string[]` convention. |
186
+ | v0.2 `status`/`stale_after` | Both read verbatim when present; an unrecognized `status` is ignored, not guessed at. |
187
+ | D2 promotion-path stamping | An accepted AKM-native proposal's written frontmatter carries `generated`/`verified` (and `sources` when `evidenceSources` was present); an OKF-adapter target is still rejected before any write (Rule 5 / write-rejection contract unchanged). |
188
+ | Links | Root-relative, relative, reference-style, and dangling links survive persistence. |
189
+ | Unknown values | Unknown type/version/key never rejects the bundle. |
190
+ | Writes | OKF writes are either blocked or produce conformant documents through adapter-owned placement. |
191
+ | Reserved names | Concept writers reject `index` and `log` at every depth. |
192
+ | Lint | A dangling OKF body link remains non-blocking under `--fail-on-flagged`. |
193
+
194
+ Run those new files explicitly in addition to the existing suite. Do not rely
195
+ only on test names containing `conformance`; the original conformance suite
196
+ tested adapter-local folds, not durable behavior.
197
+
198
+ ## 5. Initialize Container-Local AKM State
199
+
200
+ ```bash
201
+ docker exec "$AKM_OKF_CONTAINER" akm bundle create \
202
+ --dir /tmp/akm-home/akm \
203
+ --set-default
204
+ ```
205
+
206
+ Pass condition: config is written under `/tmp/akm-home`, not a mounted host
207
+ path.
208
+
209
+ ## 6. Evaluate Google's GA4 Bundle
210
+
211
+ Add and fully index the real producer output:
212
+
213
+ ```bash
214
+ docker exec "$AKM_OKF_CONTAINER" akm bundle add \
215
+ /tmp/kc/okf/bundles/ga4 \
216
+ --name okf-ga4
217
+ docker exec "$AKM_OKF_CONTAINER" akm index --full
218
+ docker exec "$AKM_OKF_CONTAINER" akm bundle list
219
+ docker exec "$AKM_OKF_CONTAINER" akm search orders \
220
+ --format json --detail full --limit 100
221
+ docker exec "$AKM_OKF_CONTAINER" akm search events \
222
+ --format json --detail full --limit 100
223
+ docker exec "$AKM_OKF_CONTAINER" akm show \
224
+ okf-ga4//tables/events_ --format json --detail full
225
+ ```
226
+
227
+ The `orders` search is informational, not a conformance gate. OKF does not
228
+ require a particular search index. The `show` command is a gate because it
229
+ tests whether the path identity emitted by the adapter remains usable through
230
+ the consumer.
231
+
232
+ Verify adapter ownership, count, type, and identity directly in the derived
233
+ index. These SQL gates target the exact v22 `entries` generation. A managed
234
+ index open discards any generation whose version or complete schema fingerprint
235
+ does not match v22, then `akm index --full` repopulates the canonical table from
236
+ source. Reader-only openers reject an incompatible generation instead of
237
+ serving legacy columns.
238
+
239
+ ```bash
240
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
241
+ set -eu
242
+ db=/tmp/akm-home/.local/share/akm/index.db
243
+ root=/tmp/kc/okf/bundles/ga4
244
+ version=$(sqlite3 "$db" "SELECT value FROM index_meta WHERE key='"'"'version'"'"';")
245
+ columns=$(sqlite3 "$db" "SELECT group_concat(name, '"'"','"'"') FROM (SELECT name FROM pragma_table_xinfo('"'"'entries'"'"') ORDER BY cid);")
246
+ expected=$(find "$root" -type f -name "*.md" ! -iname "index.md" ! -iname "log.md" | wc -l)
247
+ actual=$(sqlite3 "$db" "SELECT count(*) FROM entries WHERE bundle_id='"'"'okf-ga4'"'"';")
248
+ adapter=$(sqlite3 "$db" "SELECT group_concat(DISTINCT adapter_id) FROM entries WHERE bundle_id='"'"'okf-ga4'"'"';")
249
+ printf "schema=v%s columns=%s expected_concepts=%s actual_concepts=%s adapter=%s\n" "$version" "$columns" "$expected" "$actual" "$adapter"
250
+ test "$version" = "22"
251
+ test "$columns" = "id,item_ref,bundle_id,component_id,concept_id,adapter_id,type,file_path,content_hash,document_json,search_text,derived_from"
252
+ test "$expected" -eq "$actual"
253
+ test "$adapter" = "okf"
254
+ sqlite3 -header -column "$db" "SELECT item_ref, concept_id, type, adapter_id, file_path FROM entries WHERE bundle_id='"'"'okf-ga4'"'"' ORDER BY item_ref;"
255
+ '
256
+ ```
257
+
258
+ Pass conditions:
259
+
260
+ - `index_meta.version` is `22` and `entries` has exactly the canonical v22
261
+ column sequence shown above.
262
+ - Expected and actual counts match.
263
+ - Every row has `adapter_id=okf`.
264
+ - `item_ref` is `okf-ga4//<concept_id>`.
265
+ - `BigQuery Dataset`, `BigQuery Table`, and `Reference` survive unchanged.
266
+ - No `index.md` or `log.md` row exists.
267
+ - `akm show okf-ga4//tables/events_` succeeds.
268
+
269
+ Verify that search emits path refs rather than title refs:
270
+
271
+ ```bash
272
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
273
+ set -eu
274
+ akm search events --format json --detail full --limit 100 > /tmp/ga4-search.json
275
+ bun -e '\''import fs from "node:fs"; const r=JSON.parse(fs.readFileSync("/tmp/ga4-search.json","utf8")); const h=r.hits.find((x)=>x.path.endsWith("/tables/events_.md")); if(!h) throw new Error("events_ hit missing"); if(h.ref!=="okf-ga4//tables/events_") throw new Error(`wrong ref: ${h.ref}`);'\''
276
+ '
277
+ ```
278
+
279
+ ## 7. Create Adversarial Bundles Inside Docker
280
+
281
+ Create all fixtures inside the container. Nothing is written to the host.
282
+
283
+ ```bash
284
+ docker exec -i "$AKM_OKF_CONTAINER" sh -lc 'tee /tmp/create-okf-fixtures.mjs >/dev/null' <<'FIXTURE_SCRIPT'
285
+ import fs from "node:fs";
286
+ import path from "node:path";
287
+
288
+ function write(root, rel, content) {
289
+ const dest = path.join(root, rel);
290
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
291
+ fs.writeFileSync(dest, content);
292
+ }
293
+
294
+ const root = "/tmp/okf-adversarial";
295
+ write(root, "index.md", `---
296
+ okf_version: "9.9"
297
+ ---
298
+
299
+ # Adversarial bundle
300
+ `);
301
+ write(root, "log.md", `# Update Log
302
+
303
+ ## 2026-07-23
304
+ * **Update**: Created the evaluation bundle.
305
+ `);
306
+ write(root, "sub/index.md", `# Subdirectory
307
+
308
+ * [Duplicate](duplicate-b.md)
309
+ `);
310
+ write(root, "plain.md", `# Plain
311
+
312
+ No frontmatter. Marker: plain-body-only.
313
+ `);
314
+ write(root, "notype.md", `---
315
+ title: No Type
316
+ optional_vendor_key: retained-if-roundtripped
317
+ ---
318
+
319
+ Missing type. Marker: no-type-body-only.
320
+ `);
321
+ write(root, "unknown.md", `---
322
+ type: Some Vendor Thing
323
+ title: Vendor Item
324
+ vendor_meta:
325
+ owner: vendor
326
+ revision: 7
327
+ ---
328
+
329
+ # Overview
330
+
331
+ Marker: vendor-body-only.
332
+
333
+ [Root](/target.md)
334
+ [Relative](./relative.md)
335
+ [Dangling](/missing.md)
336
+ [Reference style][vendor-reference]
337
+
338
+ [vendor-reference]: /ref-target.md
339
+ `);
340
+ write(root, "target.md", `---
341
+ type: Known
342
+ title: Root Target
343
+ ---
344
+
345
+ Target body.
346
+ `);
347
+ write(root, "relative.md", `---
348
+ type: Known
349
+ title: Relative Target
350
+ ---
351
+
352
+ Relative body.
353
+ `);
354
+ write(root, "ref-target.md", `---
355
+ type: Known
356
+ title: Reference Target
357
+ ---
358
+
359
+ Reference target body.
360
+ `);
361
+ write(root, "duplicate-a.md", `---
362
+ type: Vendor Duplicate
363
+ title: Same Title
364
+ ---
365
+
366
+ First concept.
367
+ `);
368
+ write(root, "sub/duplicate-b.md", `---
369
+ type: Vendor Duplicate
370
+ title: Same Title
371
+ ---
372
+
373
+ Second concept.
374
+ `);
375
+ write(root, ".hidden/hidden.md", `---
376
+ type: Hidden Concept
377
+ title: Hidden Concept
378
+ ---
379
+
380
+ Hidden directory concept.
381
+ `);
382
+ write(root, "bin/bin-doc.md", `---
383
+ type: Bin Concept
384
+ title: Bin Concept
385
+ ---
386
+
387
+ Bin directory concept.
388
+ `);
389
+ write(root, "knowledge/roundtrip.md", `---
390
+ type: Some Vendor Thing
391
+ title: Round Trip
392
+ vendor_meta:
393
+ owner: preserve-me
394
+ nested:
395
+ value: 42
396
+ ---
397
+
398
+ Round-trip body marker.
399
+ `);
400
+ // v0.2 update (#730): the full trust/provenance/lifecycle family in one
401
+ // concept — generated (supersedes legacy timestamp), verified (list form),
402
+ // object-list sources, status, stale_after.
403
+ write(root, "knowledge/v2-full.md", `---
404
+ type: Some Vendor Thing
405
+ title: V0.2 Full Family
406
+ generated:
407
+ by: reference_agent/gemini-2.5-pro
408
+ at: 2026-06-20T22:53:05Z
409
+ verified:
410
+ - by: human:ahormati
411
+ at: 2026-06-25T09:00:00Z
412
+ - by: process:finance-nightly
413
+ at: 2026-06-26T03:00:00Z
414
+ sources:
415
+ - resource: https://example.com/data/export.csv
416
+ id: main-dataset
417
+ title: Example export
418
+ author: human:jdoe
419
+ usage_count: 3
420
+ last_modified: "2026-06-01"
421
+ - resource: gs://acme-bucket/raw.parquet
422
+ status: stable
423
+ stale_after: "2026-12-31"
424
+ ---
425
+
426
+ V0.2 full-family body marker.
427
+ `);
428
+ // v0.2 update (#730): verified SINGLE-MAPPING form (no list dash) + draft status.
429
+ write(root, "knowledge/v2-verified-single.md", `---
430
+ type: Some Vendor Thing
431
+ title: V0.2 Verified Single Mapping
432
+ generated:
433
+ by: human:ahormati
434
+ at: 2026-06-18T00:00:00Z
435
+ verified:
436
+ by: human:ahormati
437
+ at: 2026-06-18T00:05:00Z
438
+ status: draft
439
+ ---
440
+
441
+ V0.2 single-mapping verified body marker.
442
+ `);
443
+
444
+ const noIndex = "/tmp/okf-no-index";
445
+ write(noIndex, "vendor.md", `---
446
+ type: Some Vendor Thing
447
+ title: No Index Vendor
448
+ vendor_key: keep
449
+ ---
450
+
451
+ No-index marker.
452
+ `);
453
+
454
+ const overlap = "/tmp/okf-wiki-overlap";
455
+ write(overlap, "index.md", "# Root index\n");
456
+ write(overlap, "schema.md", "# Wiki schema\n");
457
+ write(overlap, "pages/page.md", `---
458
+ page_kind: concept
459
+ ---
460
+
461
+ # Wiki page
462
+ `);
463
+
464
+ const lintRoot = "/tmp/okf-lint-dangling";
465
+ write(lintRoot, "index.md", "# Root index\n");
466
+ write(lintRoot, "concepts/source.md", `---
467
+ type: Reference
468
+ title: Dangling Source
469
+ ---
470
+
471
+ [Future concept](/concepts/not-yet-written.md)
472
+ `);
473
+ FIXTURE_SCRIPT
474
+ docker exec "$AKM_OKF_CONTAINER" bun /tmp/create-okf-fixtures.mjs
475
+ ```
476
+
477
+ Add the bundles and rebuild:
478
+
479
+ ```bash
480
+ docker exec "$AKM_OKF_CONTAINER" akm bundle add \
481
+ /tmp/okf-adversarial --name adversarial
482
+ docker exec "$AKM_OKF_CONTAINER" akm bundle add \
483
+ /tmp/okf-no-index --name noindex
484
+ docker exec "$AKM_OKF_CONTAINER" akm bundle add \
485
+ /tmp/okf-wiki-overlap --name overlap
486
+ docker exec "$AKM_OKF_CONTAINER" akm bundle add \
487
+ /tmp/okf-lint-dangling --name lint-dangling
488
+ docker exec "$AKM_OKF_CONTAINER" akm index --full
489
+ ```
490
+
491
+ ## 8. Assert Recognition And Tolerance
492
+
493
+ Run the database assertions:
494
+
495
+ ```bash
496
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
497
+ set -eu
498
+ db=/tmp/akm-home/.local/share/akm/index.db
499
+
500
+ test "$(sqlite3 "$db" "SELECT group_concat(DISTINCT adapter_id) FROM entries WHERE bundle_id='"'"'adversarial'"'"';")" = "okf"
501
+ test "$(sqlite3 "$db" "SELECT group_concat(DISTINCT adapter_id) FROM entries WHERE bundle_id='"'"'noindex'"'"';")" = "okf"
502
+ test "$(sqlite3 "$db" "SELECT group_concat(DISTINCT adapter_id) FROM entries WHERE bundle_id='"'"'overlap'"'"';")" = "llm-wiki"
503
+
504
+ test "$(sqlite3 "$db" "SELECT type FROM entries WHERE item_ref='"'"'adversarial//unknown'"'"';")" = "Some Vendor Thing"
505
+ test "$(sqlite3 "$db" "SELECT type FROM entries WHERE item_ref='"'"'noindex//vendor'"'"';")" = "Some Vendor Thing"
506
+
507
+ test "$(sqlite3 "$db" "SELECT count(*) FROM entries WHERE bundle_id='"'"'adversarial'"'"' AND type='"'"'Vendor Duplicate'"'"';")" -eq 2
508
+ test "$(sqlite3 "$db" "SELECT count(*) FROM entries WHERE item_ref IN ('"'"'adversarial//.hidden/hidden'"'"','"'"'adversarial//bin/bin-doc'"'"');")" -eq 2
509
+ test "$(sqlite3 "$db" "SELECT count(*) FROM entries WHERE bundle_id='"'"'adversarial'"'"' AND lower(file_path) GLOB '"'"'*/*index.md'"'"';")" -eq 0
510
+ test "$(sqlite3 "$db" "SELECT count(*) FROM entries WHERE bundle_id='"'"'adversarial'"'"' AND lower(file_path) GLOB '"'"'*/*log.md'"'"';")" -eq 0
511
+
512
+ sqlite3 -header -column "$db" "SELECT item_ref,type,adapter_id FROM entries WHERE item_ref IN ('"'"'adversarial//plain'"'"','"'"'adversarial//notype'"'"') ORDER BY item_ref;"
513
+ sqlite3 -header -column "$db" "SELECT bundle_id,item_ref,type,adapter_id,file_path FROM entries WHERE bundle_id IN ('"'"'adversarial'"'"','"'"'noindex'"'"','"'"'overlap'"'"') ORDER BY bundle_id,item_ref;"
514
+ '
515
+ ```
516
+
517
+ These assertions are permanent gates for the no-index source, duplicate title
518
+ pair, hidden directory, and `bin` directory. A change is not complete until
519
+ each conformant path-defined concept survives independently.
520
+ The plain and missing-type rows are printed for observation but are not gates:
521
+ those files are nonconformant producers, and OKF does not require a consumer to
522
+ accept a concept whose required `type` is absent.
523
+
524
+ v0.2 update (#730) — assert the trust/provenance/lifecycle family on the two
525
+ new fixtures (Rules 10-13):
526
+
527
+ ```bash
528
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
529
+ set -eu
530
+ db=/tmp/akm-home/.local/share/akm/index.db
531
+
532
+ full=$(sqlite3 "$db" "SELECT document_json FROM entries WHERE item_ref='"'"'adversarial//knowledge/v2-full'"'"';")
533
+ bun -e '\''
534
+ const e = JSON.parse(process.argv[1]);
535
+ if (e.updated !== "2026-06-20T22:53:05Z") throw new Error(`generated.at not preferred: ${e.updated}`);
536
+ if (e.provenance?.generatedBy !== "reference_agent/gemini-2.5-pro") throw new Error("generatedBy missing");
537
+ if (!Array.isArray(e.provenance?.verified) || e.provenance.verified.length !== 2) throw new Error("verified list wrong length");
538
+ if (!Array.isArray(e.provenance?.sources) || e.provenance.sources.length !== 2) throw new Error("sources object-list wrong length");
539
+ if (e.provenance.sources[0].resource !== "https://example.com/data/export.csv") throw new Error("sources[0].resource wrong");
540
+ if (e.lifecycleStatus !== "stable") throw new Error(`lifecycleStatus wrong: ${e.lifecycleStatus}`);
541
+ if (e.staleAfter !== "2026-12-31") throw new Error(`staleAfter wrong: ${e.staleAfter}`);
542
+ // The pre-existing AKM-native sources:string[] field (wiki citations) must
543
+ // NEVER be populated by the okf adapter -- it is a completely different field.
544
+ if (e.sources !== undefined) throw new Error("bare sources field must stay unset for okf-adapter documents");
545
+ '\'' "$full"
546
+
547
+ single=$(sqlite3 "$db" "SELECT document_json FROM entries WHERE item_ref='"'"'adversarial//knowledge/v2-verified-single'"'"';")
548
+ bun -e '\''
549
+ const e = JSON.parse(process.argv[1]);
550
+ if (!Array.isArray(e.provenance?.verified) || e.provenance.verified.length !== 1) throw new Error("single-mapping verified did not normalize to a one-element array");
551
+ if (e.provenance.verified[0].by !== "human:ahormati") throw new Error("verified[0].by wrong");
552
+ if (e.lifecycleStatus !== "draft") throw new Error(`lifecycleStatus wrong: ${e.lifecycleStatus}`);
553
+ '\'' "$single"
554
+ '
555
+ ```
556
+
557
+ Pass condition: both `bun -e` checks above exit zero (no thrown error).
558
+
559
+ Verify unknown type filtering and path-based show:
560
+
561
+ ```bash
562
+ docker exec "$AKM_OKF_CONTAINER" akm search Vendor \
563
+ --type 'Some Vendor Thing' \
564
+ --format json --detail full --limit 100
565
+ docker exec "$AKM_OKF_CONTAINER" akm show \
566
+ adversarial//unknown --format json --detail full
567
+ docker exec "$AKM_OKF_CONTAINER" akm show \
568
+ 'adversarial//unknown#overview' --format json --detail full
569
+ docker exec "$AKM_OKF_CONTAINER" akm show \
570
+ noindex//vendor --format json --detail full
571
+ ```
572
+
573
+ Pass condition: both `show` calls succeed without requiring an AKM type-directory
574
+ prefix.
575
+
576
+ Verify the search ref for the unknown-type document:
577
+
578
+ ```bash
579
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
580
+ set -eu
581
+ akm search Vendor --format json --detail full --limit 100 > /tmp/vendor-search.json
582
+ bun -e '\''import fs from "node:fs"; const r=JSON.parse(fs.readFileSync("/tmp/vendor-search.json","utf8")); const h=r.hits.find((x)=>x.path==="/tmp/okf-adversarial/unknown.md"); if(!h) throw new Error("vendor hit missing"); if(h.ref!=="adversarial//unknown") throw new Error(`wrong ref: ${h.ref}`);'\''
583
+ '
584
+ ```
585
+
586
+ ## 9. Assert Durable Link Semantics
587
+
588
+ The adversarial `unknown.md` has four distinct concept targets:
589
+
590
+ - `target` from a bundle-root-relative inline link.
591
+ - `relative` from a relative inline link.
592
+ - `missing` from a dangling bundle-root-relative inline link.
593
+ - `ref-target` from a reference-style Markdown link.
594
+
595
+ The dangling target must remain represented as an edge even though no target
596
+ document exists.
597
+
598
+ The current `IndexDocument` contract has a first-class `links` field, so the
599
+ minimal persistence acceptance test is:
600
+
601
+ ```bash
602
+ docker exec "$AKM_OKF_CONTAINER" sh -lc '
603
+ set -eu
604
+ db=/tmp/akm-home/.local/share/akm/index.db
605
+ links=$(sqlite3 "$db" "SELECT json_extract(document_json,'"'"'$.links'"'"') FROM entries WHERE item_ref='"'"'adversarial//unknown'"'"';")
606
+ printf "durable_links=%s\n" "$links"
607
+ bun -e '\''const links=JSON.parse(process.argv[1]); const want=["target","relative","missing","ref-target"]; for(const x of want) if(!links.includes(x)) throw new Error(`missing link ${x}`); if(links.length!==want.length) throw new Error(`unexpected links ${JSON.stringify(links)}`);'\'' "$links"
608
+ '
609
+ ```
610
+
611
+ If the implementation intentionally moves links into a dedicated relation
612
+ table, replace this assertion with an equivalent query against that table and
613
+ retain a test proving all four directed edges. Do not count LLM-extracted entity
614
+ relations as OKF concept links.
615
+
616
+ ## 10. Evaluate Writable OKF Targets And Round-Trip Behavior
617
+
618
+ AKM has two conformant policies for an OKF source. The 0.9.0 `okf` adapter uses
619
+ the consumer-only policy:
620
+
621
+ | Policy | Required behavior |
622
+ |---|---|
623
+ | Consumer-only/read-only | The write command fails before creating or changing a file. |
624
+ | Writable OKF | Placement is adapter-owned, every new concept is conformant, and metadata-only edits preserve unknown keys and body bytes. |
625
+
626
+ **v0.2 update (#730) — Rule 14 scope note:** the assertions below are entirely
627
+ about writes *targeting an OKF-adapter bundle* (`adversarial`), and that
628
+ policy is unchanged by the v0.2 update — the write command below must still
629
+ fail before touching disk. AKM's new v0.2 provenance-stamping behavior (D2)
630
+ is a completely separate code path: accepting a proposal stamps
631
+ `generated`/`verified` frontmatter onto an **AKM-native** target through
632
+ `promoteProposal`, never through the `okf` adapter — it is orthogonal to
633
+ every assertion in this section. This runbook does not script a manual D2
634
+ spot-check (the `akm proposal`/`akm remember` CLI surface for driving one
635
+ deterministically end-to-end was not re-verified against this container in
636
+ this update); `tests/integration/proposals.test.ts`'s `"D2 (#730): ..."`
637
+ suite is the authoritative, already-passing coverage for Rule 14 — rerun it
638
+ if you need to re-confirm this specific behavior:
639
+ `bun test tests/integration/proposals.test.ts -t D2`.
640
+
641
+ Attempt a superseding write while recording its exit status:
642
+
643
+ ```bash
644
+ set +e
645
+ docker exec "$AKM_OKF_CONTAINER" akm remember \
646
+ 'Correction written during OKF conformance evaluation' \
647
+ --name correction \
648
+ --bundle adversarial \
649
+ --supersedes adversarial//knowledge/roundtrip
650
+ export OKF_WRITE_STATUS=$?
651
+ set -e
652
+ printf 'okf_write_status=%s\n' "$OKF_WRITE_STATUS"
653
+ ```
654
+
655
+ If the status is non-zero, verify that no correction was created and record the
656
+ source as consumer-only/read-only.
657
+
658
+ ```bash
659
+ if test "$OKF_WRITE_STATUS" -ne 0; then
660
+ docker exec "$AKM_OKF_CONTAINER" test ! -e /tmp/okf-adversarial/memories/correction.md
661
+ fi
662
+ ```
663
+
664
+ If the status is zero, verify the old document's unknown nested key, type, and
665
+ body survived, and verify the new document has a non-empty `type`:
666
+
667
+ ```bash
668
+ if test "$OKF_WRITE_STATUS" -eq 0; then
669
+ docker exec "$AKM_OKF_CONTAINER" bun -e '
670
+ import fs from "node:fs";
671
+ import { parse } from "yaml";
672
+ function split(raw) {
673
+ const m=raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/);
674
+ if(!m) throw new Error("frontmatter missing");
675
+ return {fm:parse(m[1]),body:m[2]};
676
+ }
677
+ const old=split(fs.readFileSync("/tmp/okf-adversarial/knowledge/roundtrip.md","utf8"));
678
+ if(old.fm.type!=="Some Vendor Thing") throw new Error("type changed");
679
+ if(old.fm.vendor_meta?.owner!=="preserve-me") throw new Error("unknown key lost");
680
+ if(old.fm.vendor_meta?.nested?.value!==42) throw new Error("nested unknown key lost");
681
+ if(!old.body.includes("Round-trip body marker.")) throw new Error("body changed");
682
+ const created=split(fs.readFileSync("/tmp/okf-adversarial/memories/correction.md","utf8"));
683
+ if(typeof created.fm.type!=="string" || !created.fm.type.trim()) throw new Error("new OKF concept has no type");
684
+ '
685
+ fi
686
+ ```
687
+
688
+ Test that concept writers reject reserved filenames. This must fail whether the
689
+ source is generally writable or consumer-only:
690
+
691
+ ```bash
692
+ RESERVED_BEFORE=$(docker exec "$AKM_OKF_CONTAINER" sh -lc 'find /tmp/okf-adversarial -type f \( -iname "index.md" -o -iname "log.md" \) -exec sha256sum {} + | sort | sha256sum')
693
+ set +e
694
+ docker exec "$AKM_OKF_CONTAINER" akm remember \
695
+ 'Reserved index payload' --name index --bundle adversarial
696
+ INDEX_WRITE_STATUS=$?
697
+ docker exec "$AKM_OKF_CONTAINER" akm remember \
698
+ 'Reserved log payload' --name log --bundle adversarial
699
+ LOG_WRITE_STATUS=$?
700
+ set -e
701
+ test "$INDEX_WRITE_STATUS" -ne 0
702
+ test "$LOG_WRITE_STATUS" -ne 0
703
+ RESERVED_AFTER=$(docker exec "$AKM_OKF_CONTAINER" sh -lc 'find /tmp/okf-adversarial -type f \( -iname "index.md" -o -iname "log.md" \) -exec sha256sum {} + | sort | sha256sum')
704
+ test "$RESERVED_BEFORE" = "$RESERVED_AFTER"
705
+ docker exec "$AKM_OKF_CONTAINER" test ! -e /tmp/okf-adversarial/memories/index.md
706
+ docker exec "$AKM_OKF_CONTAINER" test ! -e /tmp/okf-adversarial/memories/log.md
707
+ ```
708
+
709
+ A future structural index/log generator is allowed to write those files through
710
+ an explicitly structural path. The ban applies to concept writers, not to valid
711
+ structural production.
712
+
713
+ ## 11. Evaluate Lint
714
+
715
+ Run lint against a conformant bundle whose only issue is a dangling body link:
716
+
717
+ ```bash
718
+ docker exec "$AKM_OKF_CONTAINER" akm lint \
719
+ --dir /tmp/okf-lint-dangling \
720
+ --fail-on-flagged
721
+ ```
722
+
723
+ Also run lint against the official bundle:
724
+
725
+ ```bash
726
+ docker exec "$AKM_OKF_CONTAINER" akm lint \
727
+ --dir /tmp/kc/okf/bundles/ga4 \
728
+ --fail-on-flagged
729
+ ```
730
+
731
+ Pass condition: both commands exit zero. The adapter-aware OKF lint walk checks
732
+ all concept paths, including ordinary dot directories and `bin/`. It may report
733
+ the dangling target as informational or non-blocking, but
734
+ `--fail-on-flagged` must not turn that spec-tolerated link into CI failure.
735
+
736
+ ## 12. Optional Producer Validation
737
+
738
+ Skip this section if AKM explicitly remains consumer-only. Do not classify the
739
+ absence of a producer as a consumer failure.
740
+
741
+ If AKM adds a producer, save this checker inside the container:
742
+
743
+ ```bash
744
+ docker exec -i "$AKM_OKF_CONTAINER" sh -lc 'tee /tmp/check-okf-producer.mjs >/dev/null' <<'CHECKER'
745
+ import fs from "node:fs";
746
+ import path from "node:path";
747
+ import { parse } from "yaml";
748
+
749
+ const root = path.resolve(process.argv[2]);
750
+ const errors = [];
751
+
752
+ function frontmatter(raw) {
753
+ const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/);
754
+ if (!m) return null;
755
+ try {
756
+ const data = parse(m[1]);
757
+ return data && typeof data === "object" && !Array.isArray(data)
758
+ ? { data, body: m[2] }
759
+ : null;
760
+ } catch {
761
+ return null;
762
+ }
763
+ }
764
+
765
+ function walk(dir) {
766
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
767
+ const abs = path.join(dir, entry.name);
768
+ if (entry.isDirectory()) {
769
+ walk(abs);
770
+ continue;
771
+ }
772
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".md")) continue;
773
+ const rel = path.relative(root, abs).replaceAll(path.sep, "/");
774
+ const lower = entry.name.toLowerCase();
775
+ const raw = fs.readFileSync(abs, "utf8");
776
+ if (lower === "index.md") {
777
+ const fm = frontmatter(raw);
778
+ if (fm && rel !== "index.md") errors.push(`${rel}: nested index has frontmatter`);
779
+ if (fm && Object.keys(fm.data).some((key) => key !== "okf_version")) {
780
+ errors.push(`${rel}: root index frontmatter contains keys other than okf_version`);
781
+ }
782
+ continue;
783
+ }
784
+ if (lower === "log.md") {
785
+ for (const line of raw.split(/\r?\n/)) {
786
+ if (line.startsWith("## ") && !/^## \d{4}-\d{2}-\d{2}$/.test(line)) {
787
+ errors.push(`${rel}: invalid log date heading ${line}`);
788
+ }
789
+ }
790
+ continue;
791
+ }
792
+ const fm = frontmatter(raw);
793
+ if (!fm) {
794
+ errors.push(`${rel}: missing or invalid mapping frontmatter`);
795
+ continue;
796
+ }
797
+ if (typeof fm.data.type !== "string" || !fm.data.type.trim()) {
798
+ errors.push(`${rel}: missing non-empty type`);
799
+ }
800
+ }
801
+ }
802
+
803
+ if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
804
+ throw new Error(`bundle not found: ${root}`);
805
+ }
806
+ walk(root);
807
+ if (errors.length) {
808
+ for (const error of errors) console.error(error);
809
+ process.exit(1);
810
+ }
811
+ console.log(`OKF producer check passed: ${root}`);
812
+ CHECKER
813
+ ```
814
+
815
+ Generate a bundle through the public AKM producer command, then run:
816
+
817
+ ```bash
818
+ docker exec "$AKM_OKF_CONTAINER" bun \
819
+ /tmp/check-okf-producer.mjs /path/to/generated/bundle
820
+ ```
821
+
822
+ The producer check is intentionally independent of AKM's parser. A producer
823
+ must not validate itself with the same lenient parser used by its consumer.
824
+
825
+ ## 13. Run Repository Verification
826
+
827
+ After focused behavior passes, run the repository's normal checks inside the
828
+ same image:
829
+
830
+ ```bash
831
+ docker exec "$AKM_OKF_CONTAINER" bun run check
832
+ docker exec "$AKM_OKF_CONTAINER" bun run build
833
+ ```
834
+
835
+ For pre-commit verification, follow repository policy inside Docker:
836
+
837
+ ```bash
838
+ docker exec "$AKM_OKF_CONTAINER" bunx biome check --write src/ tests/
839
+ docker exec "$AKM_OKF_CONTAINER" bun run check
840
+ ```
841
+
842
+ The write-capable Biome command changes only the container copy. If it reports
843
+ changes, apply the equivalent formatting to the host working tree deliberately
844
+ and rebuild the image before treating the result as final.
845
+
846
+ ## 14. Record The New Verdict
847
+
848
+ Create a result record containing:
849
+
850
+ ```text
851
+ AKM commit and dirty state:
852
+ AKM version:
853
+ OKF repository commit (KC_REF):
854
+ Docker base image:
855
+ Focused tests:
856
+ Full check:
857
+ Official GA4 expected/actual concept count:
858
+ Official adapter IDs:
859
+ Official type values:
860
+ Official show-by-concept-ID result:
861
+ Adversarial adapter ID:
862
+ Missing-index adapter ID:
863
+ Overlap precedence result:
864
+ Duplicate-title row count:
865
+ Hidden/bin concept result:
866
+ Unknown type result:
867
+ Unknown version result:
868
+ Durable link targets:
869
+ Dangling-link indexing result:
870
+ Unknown-key round-trip result or N/A:
871
+ Reserved concept-write result:
872
+ Producer result or consumer-only:
873
+ Lint --fail-on-flagged result:
874
+ v0.2 generated.at precedence result:
875
+ v0.2 verified (list form) result:
876
+ v0.2 verified (single-mapping form) result:
877
+ v0.2 sources (object list) result:
878
+ v0.2 status/stale_after result:
879
+ D2 promotion-path provenance-stamping result or N/A:
880
+ Rule 1 verdict:
881
+ Rule 2 verdict:
882
+ Rule 3 verdict:
883
+ Rule 4 verdict:
884
+ Rule 5 verdict:
885
+ Rule 6 verdict:
886
+ Rule 7 verdict:
887
+ Rule 8 verdict:
888
+ Rule 9 verdict:
889
+ Rule 10 verdict:
890
+ Rule 11 verdict:
891
+ Rule 12 verdict:
892
+ Rule 13 verdict:
893
+ Rule 14 verdict:
894
+ Remaining deviations:
895
+ Remaining undetermined items:
896
+ ```
897
+
898
+ Do not mark a rule satisfied from a direct adapter unit test if the durable or
899
+ CLI path still loses the behavior. Do not mark Rule 5 satisfied merely because
900
+ unknown keys did not cause a parse error. Likewise, do not mark Rules 10-13
901
+ satisfied from `tests/core/adapter/okf-adapter.test.ts` alone if the durable
902
+ persist path (`indexer/scan/doc-to-entry.ts`) or the `show`/`search` output
903
+ loses a field — this repo has a documented history (D1's own GREEN commit) of
904
+ `recognize()` returning a field correctly while a separate persistence-layer
905
+ reconstruction step silently dropped it before it ever reached `document_json`.
906
+
907
+ ## 15. Cleanup
908
+
909
+ Remove all custom containers and images:
910
+
911
+ ```bash
912
+ docker rm -f "$AKM_OKF_CONTAINER"
913
+ docker image rm "$AKM_OKF_IMAGE"
914
+ ```
915
+
916
+ Optional verification that no host AKM state changed:
917
+
918
+ ```bash
919
+ git status --short
920
+ ```
921
+
922
+ Only intentional source/test/documentation changes should appear. No
923
+ `~/.config/akm`, `~/.cache/akm`, or local AKM index should have been read or
924
+ modified by this runbook.