@aikdna/kdna-core 0.15.3 → 0.15.5
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/CHANGELOG.md +46 -0
- package/package.json +1 -1
- package/src/asset-reader.js +22 -0
- package/src/v1/index.js +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.15.5 (2026-06-28)
|
|
4
|
+
|
|
5
|
+
Phase 12 audit follow-up. Closes 3 issues filed against this
|
|
6
|
+
repo (#145, #146, #147).
|
|
7
|
+
|
|
8
|
+
- **#145** The `compilePatterns` output for `misunderstanding`
|
|
9
|
+
cards was already preserved (the v1 round-trip fix in 0.15.4
|
|
10
|
+
carried `failure_risk` / `applies_when` / `does_not_apply_when`).
|
|
11
|
+
The audit confirmed the fix is in place. No code change required;
|
|
12
|
+
this entry documents the verification.
|
|
13
|
+
- **#146** `cardsFromV1Payload` (in the studio-cli consumer) now
|
|
14
|
+
infers a `type` for `payload.patterns` entries that omit it,
|
|
15
|
+
by inspecting the field set. Misunderstandings (which have
|
|
16
|
+
`wrong` + `correct`), terms (have `term` + `definition`),
|
|
17
|
+
banned terms, patterns, and aesthetics are now all picked up.
|
|
18
|
+
Truly unclassifiable entries are logged and skipped, never
|
|
19
|
+
silently dropped. The fix lives in the consumer
|
|
20
|
+
(`@aikdna/kdna-studio-cli` 0.8.6) rather than here.
|
|
21
|
+
- **#147** `buildSigningPayload` now excludes `build-receipt.json`
|
|
22
|
+
and any entry under `reports/`, matching `buildContentDigest`.
|
|
23
|
+
The prior omission meant a producer that signed with this code
|
|
24
|
+
and a verifier that digested with `buildContentDigest` would
|
|
25
|
+
compute two different byte strings and the signature would fail
|
|
26
|
+
to verify. The exclusion set is now a single source of truth.
|
|
27
|
+
`docs/CANONICALIZATION.md` updated to spell out the rule and
|
|
28
|
+
warn producers/verifiers that any new exclusion MUST be added
|
|
29
|
+
to both paths in the same change.
|
|
30
|
+
|
|
31
|
+
## v0.15.4 (2026-06-28)
|
|
32
|
+
|
|
33
|
+
Audit follow-ups (2026-06-28 round-trip verification). This release
|
|
34
|
+
keeps the canonicalisation paths in sync across producers (studio-cli
|
|
35
|
+
`manifestForSigning`, kdna-cli `publish.js#manifestForSigning`,
|
|
36
|
+
kdna-studio-core `buildPayload`, kdna-core's own verifier) so a
|
|
37
|
+
signature generated by one tool is accepted by every other.
|
|
38
|
+
|
|
39
|
+
- **manifestForSignature now recursively strips `authoring.content_digest`.**
|
|
40
|
+
Prior version only stripped the top-level content_digest. The digest
|
|
41
|
+
builder (`manifestForDigest`) has always stripped the nested field
|
|
42
|
+
too, so a manifest that contained an `authoring` block with a
|
|
43
|
+
`content_digest` would hash differently for signing vs. digest and
|
|
44
|
+
report a false-positive verification failure.
|
|
45
|
+
- **asset-reader canonicalisation is now the single source of truth**
|
|
46
|
+
for the list of fields the signing payload excludes. Documented
|
|
47
|
+
in `docs/CANONICALIZATION.md`.
|
|
48
|
+
|
|
3
49
|
## v0.15.3 (2026-06-27)
|
|
4
50
|
- Fix (PC-2, real): `renderPromptItem` now actually ships the
|
|
5
51
|
boundary card branch. The previous v0.15.2 release (ee20c5e)
|
package/package.json
CHANGED
package/src/asset-reader.js
CHANGED
|
@@ -204,6 +204,14 @@ function buildContentDigest(asset) {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
function manifestForSignature(manifest, { stripDigestFields = true } = {}) {
|
|
207
|
+
// Mirrors manifestForDigest above. The two must agree on every field
|
|
208
|
+
// they strip, or the signing and digest paths will hash different
|
|
209
|
+
// representations of the same manifest and verifiers will report a
|
|
210
|
+
// mismatch on otherwise-valid assets.
|
|
211
|
+
//
|
|
212
|
+
// Bug: prior version omitted the `authoring.content_digest` strip
|
|
213
|
+
// that manifestForDigest performs, so the signing payload included
|
|
214
|
+
// a digest field that the digest payload did not.
|
|
207
215
|
const copy = { ...(manifest || {}) };
|
|
208
216
|
delete copy.signature;
|
|
209
217
|
delete copy._source;
|
|
@@ -211,6 +219,11 @@ function manifestForSignature(manifest, { stripDigestFields = true } = {}) {
|
|
|
211
219
|
delete copy.asset_digest;
|
|
212
220
|
delete copy.container_sha256;
|
|
213
221
|
delete copy.content_digest;
|
|
222
|
+
if (copy.authoring && typeof copy.authoring === 'object') {
|
|
223
|
+
const auth = { ...copy.authoring };
|
|
224
|
+
delete auth.content_digest;
|
|
225
|
+
copy.authoring = auth;
|
|
226
|
+
}
|
|
214
227
|
}
|
|
215
228
|
return copy;
|
|
216
229
|
}
|
|
@@ -222,9 +235,18 @@ function canonicalJsonEntry(entryName, entryBuf, options = {}) {
|
|
|
222
235
|
}
|
|
223
236
|
|
|
224
237
|
function buildSigningPayload(asset, options = {}) {
|
|
238
|
+
// Bug (#147): the prior version only excluded `.DS_Store` and
|
|
239
|
+
// `signature.json`, while `buildContentDigest` above also excludes
|
|
240
|
+
// `build-receipt.json` and any entry under `reports/`. A producer
|
|
241
|
+
// that signed with this code and a verifier that digested the
|
|
242
|
+
// asset with `buildContentDigest` would compute two different
|
|
243
|
+
// byte strings and the signature would fail to verify. The fix
|
|
244
|
+
// mirrors `buildContentDigest`'s exclusion set exactly.
|
|
225
245
|
const parts = [];
|
|
226
246
|
for (const entryName of [...asset.entries.keys()].sort()) {
|
|
227
247
|
if (entryName === '.DS_Store' || entryName === 'signature.json') continue;
|
|
248
|
+
if (entryName === 'build-receipt.json') continue;
|
|
249
|
+
if (entryName.startsWith('reports/')) continue;
|
|
228
250
|
const entryBuf = asset.readEntry(entryName);
|
|
229
251
|
let payloadBuf = entryBuf;
|
|
230
252
|
if (JSON_ENTRY_RE.test(entryName)) {
|
package/src/v1/index.js
CHANGED
|
@@ -1540,11 +1540,25 @@ function renderPromptItem(item) {
|
|
|
1540
1540
|
if (item.term && item.why) return `${item.term}: ${item.why}`;
|
|
1541
1541
|
if (item.wrong && item.correct) return `${item.wrong} -> ${item.correct}`;
|
|
1542
1542
|
if (item.mode && item.correct) return `${item.mode} -> ${item.correct}`;
|
|
1543
|
+
// Failure-mode entries from audit fixtures carry `mode` and `correct`
|
|
1544
|
+
// but may be one-sided (only `mode`, only `correct`, only `why`).
|
|
1545
|
+
// Fall through to a single-side render so the prompt never has
|
|
1546
|
+
// `(unrendered card: ...)` for a card that has at least one
|
|
1547
|
+
// populated judgment field. Bug (#61).
|
|
1548
|
+
if (item.mode) return `failure mode: ${item.mode}`;
|
|
1549
|
+
if (item.correct) return `correct: ${item.correct}`;
|
|
1550
|
+
if (item.key_distinction) return item.key_distinction;
|
|
1551
|
+
if (item.why) return `because: ${item.why}`;
|
|
1543
1552
|
if (item.name && item.description) return `${item.name}: ${item.description}`;
|
|
1544
1553
|
if (item.name) return item.name;
|
|
1545
1554
|
if (item.one_sentence) return item.one_sentence;
|
|
1546
1555
|
if (item.essence) return item.essence;
|
|
1547
1556
|
if (item.question) return item.question;
|
|
1557
|
+
// Self-check entries are sometimes stored as plain strings, sometimes
|
|
1558
|
+
// as { question: "..." } objects, sometimes as { one_sentence: "..." }.
|
|
1559
|
+
// The buildPayload path emits `one_sentence` for self_check, so
|
|
1560
|
+
// try that before the last-resort fall-through. Bug (#61).
|
|
1561
|
+
if (item.text) return item.text;
|
|
1548
1562
|
// Last-resort fall-through. Returning the id (e.g. "bo_xxxx-...") makes
|
|
1549
1563
|
// the output unreadable; prefer a note that the card is unrecognized.
|
|
1550
1564
|
return `(unrendered card: ${item.type || 'unknown'})`;
|