@riddledc/riddle-proof-packs 0.4.2 → 0.4.4

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 (23) hide show
  1. package/README.md +37 -1
  2. package/dist/index.cjs +168 -2
  3. package/dist/index.d.cts +17 -1
  4. package/dist/index.d.ts +17 -1
  5. package/dist/index.js +163 -1
  6. package/package.json +1 -1
  7. package/packs/neon-step-sequencer/README.md +2 -1
  8. package/packs/neon-step-sequencer/case-study/findings.md +11 -0
  9. package/packs/neon-step-sequencer/case-study/ratchet-card.md +2 -1
  10. package/packs/neon-step-sequencer/case-study/ratchet-log.md +55 -0
  11. package/packs/neon-step-sequencer/case-study/reusable-lessons.md +4 -0
  12. package/packs/neon-step-sequencer/examples/README.md +3 -1
  13. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/artifact-manifest.json +32 -0
  14. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/console.json +4 -0
  15. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/dom-summary.json +40 -0
  16. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/human-review-packet.json +795 -0
  17. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/human-review-packet.md +49 -0
  18. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/profile-result.json +11786 -0
  19. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/proof.json +11786 -0
  20. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/screenshots/lilarcade-neon-ratchet-loop-mix-level-search-desktop-neon-ratchet-loop-mix-level-search.png +0 -0
  21. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/screenshots/lilarcade-neon-ratchet-loop-mix-level-search-desktop.png +0 -0
  22. package/packs/neon-step-sequencer/examples/run-006-ratchet-loop-human-review-packet/summary.md +44 -0
  23. package/packs/neon-step-sequencer/profiles/ratchet-loop-mix-level-search.json +29 -0
package/README.md CHANGED
@@ -24,6 +24,14 @@ Reusable starter profile definitions and proof-pack metadata for Riddle Proof.
24
24
  - Returns first manifest entry for a matching `pack_id`.
25
25
  - `instantiateRiddleProofProfile(name, options)`:
26
26
  - Returns a copy of a profile with optional `url`, `route`, and `target` overrides.
27
+ - `findHumanReviewPacket(proofOrPacket)`:
28
+ - Recursively finds the first `human_review_packet` in a proof artifact or returns `null`.
29
+ - `requireHumanReviewPacket(proofOrPacket)`:
30
+ - Same extraction behavior, but throws if no packet is present.
31
+ - `formatHumanReviewPacketMarkdown(packet, options)`:
32
+ - Formats a compact Markdown handoff with recommendation, objective receipts, ranking role, proof boundary, listening prompts, and caveats.
33
+ - `createHumanReviewPacketArtifacts(proofOrPacket, options)`:
34
+ - Returns `{ packet, json, markdown }` for storing a standalone review packet next to a proof run.
27
35
 
28
36
  ## Proof claims and evidence roles
29
37
 
@@ -90,12 +98,40 @@ Profiles are stored under `packs/<slug>/profile.json` and mirrored into the runt
90
98
  - `neon-step-sequencer-mobile-trainer-layout`
91
99
  - `neon-step-sequencer-full-mix-health-matrix`
92
100
  - `neon-step-sequencer-explore-songs-and-mixes`
101
+ - `neon-step-sequencer-ratchet-loop-mix-level-search`
93
102
 
94
103
  ## Audio and Neon ratchet packs
95
104
 
96
105
  The `audio-mix` directory contains reusable audio-proof authoring guidance, a profile template, a metrics schema, a ratchet method, and a human-review rubric.
97
106
 
98
- The `neon-step-sequencer` directory contains the first app-specific ratchet lab under the new architecture. Its profiles declare `current_target` or `interaction_snapshots` evidence-role patterns and explicitly state what they do not prove. The case-study files record the claim, evidence, failure classification, smallest layer changed, and next sharper question for each run.
107
+ The `neon-step-sequencer` directory contains the first app-specific ratchet lab under the new architecture. Its profiles declare `current_target` or `interaction_snapshots` evidence-role patterns and explicitly state what they do not prove. The ratchet-loop profile now expects a compact `humanReviewPacket` for listening handoff: supported/rejected candidates, objective guardrails, state restoration, review-order ranking, and taste caveats. The case-study files record the claim, evidence, failure classification, smallest layer changed, and next sharper question for each run.
108
+
109
+ ### Human-review packet handoff
110
+
111
+ Human-review packets are proof artifacts for subjective follow-up. They are deliberately not taste scores. A packet should say what objective receipts passed, what was preserved, which candidate is ready for listening review, and which caveats remain.
112
+
113
+ ```ts
114
+ import {
115
+ createHumanReviewPacketArtifacts,
116
+ findHumanReviewPacket,
117
+ formatHumanReviewPacketMarkdown,
118
+ } from "@riddledc/riddle-proof-packs";
119
+
120
+ const proof = JSON.parse(await fs.promises.readFile("proof.json", "utf8"));
121
+ const packet = findHumanReviewPacket(proof);
122
+ if (!packet) throw new Error("proof did not emit a human review packet");
123
+
124
+ const markdown = formatHumanReviewPacketMarkdown(packet, {
125
+ title: "Neon Human Review Packet",
126
+ });
127
+ const artifacts = createHumanReviewPacketArtifacts(proof, {
128
+ title: "Neon Human Review Packet",
129
+ });
130
+
131
+ console.log(markdown);
132
+ await fs.promises.writeFile("human-review-packet.json", artifacts.json);
133
+ await fs.promises.writeFile("human-review-packet.md", artifacts.markdown);
134
+ ```
99
135
 
100
136
  ## Usage
101
137
 
package/dist/index.cjs CHANGED
@@ -22,6 +22,9 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  RIDDLE_PROOF_PACK_MANIFEST: () => RIDDLE_PROOF_PACK_MANIFEST,
24
24
  RIDDLE_PROOF_PACK_PROFILES: () => RIDDLE_PROOF_PACK_PROFILES,
25
+ createHumanReviewPacketArtifacts: () => createHumanReviewPacketArtifacts,
26
+ findHumanReviewPacket: () => findHumanReviewPacket,
27
+ formatHumanReviewPacketMarkdown: () => formatHumanReviewPacketMarkdown,
25
28
  getPackEnabledRiddleProofPackProfiles: () => getPackEnabledRiddleProofPackProfiles,
26
29
  getRiddleProofPackProfile: () => getRiddleProofPackProfile,
27
30
  getRiddleProofPackProfileByPackId: () => getRiddleProofPackProfileByPackId,
@@ -29,7 +32,8 @@ __export(index_exports, {
29
32
  getRiddleProofProfilesByPackId: () => getRiddleProofProfilesByPackId,
30
33
  instantiateRiddleProofProfile: () => instantiateRiddleProofProfile,
31
34
  listRiddleProofPackProfiles: () => listRiddleProofPackProfiles,
32
- listRiddleProofPacks: () => listRiddleProofPacks
35
+ listRiddleProofPacks: () => listRiddleProofPacks,
36
+ requireHumanReviewPacket: () => requireHumanReviewPacket
33
37
  });
34
38
  module.exports = __toCommonJS(index_exports);
35
39
 
@@ -1695,6 +1699,7 @@ var ratchet_loop_mix_level_search_default = {
1695
1699
  path: "__NEON_MIX_PROOF__.runRatchetLoop",
1696
1700
  args: [
1697
1701
  {
1702
+ intent: "turn the chord part down a little",
1698
1703
  strategy: "mix-level-search",
1699
1704
  focusTracks: [
1700
1705
  "bass",
@@ -1729,6 +1734,15 @@ var ratchet_loop_mix_level_search_default = {
1729
1734
  },
1730
1735
  {
1731
1736
  path: "supportedClaimCandidateCount"
1737
+ },
1738
+ {
1739
+ path: "humanReviewPacket.status"
1740
+ },
1741
+ {
1742
+ path: "humanReviewPacket.recommendation.candidate.action.track"
1743
+ },
1744
+ {
1745
+ path: "humanReviewPacket.ranking.role"
1732
1746
  }
1733
1747
  ]
1734
1748
  },
@@ -1738,6 +1752,24 @@ var ratchet_loop_mix_level_search_default = {
1738
1752
  expected_value: true,
1739
1753
  timeout_ms: 1e4
1740
1754
  },
1755
+ {
1756
+ type: "assert_window_value",
1757
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.kind",
1758
+ expected_value: "human_review_packet",
1759
+ timeout_ms: 1e4
1760
+ },
1761
+ {
1762
+ type: "assert_window_value",
1763
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.ranking.role",
1764
+ expected_value: "review_order_only",
1765
+ timeout_ms: 1e4
1766
+ },
1767
+ {
1768
+ type: "assert_window_value",
1769
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.request.candidateActionsAreTransient",
1770
+ expected_value: true,
1771
+ timeout_ms: 1e4
1772
+ },
1741
1773
  {
1742
1774
  type: "screenshot",
1743
1775
  label: "neon-ratchet-loop-mix-level-search",
@@ -1785,6 +1817,7 @@ var ratchet_loop_mix_level_search_default = {
1785
1817
  "claim candidates are captured",
1786
1818
  "each candidate records claim receipts",
1787
1819
  "best supported claim candidate or human-review status is captured",
1820
+ "compact human-review packet is captured",
1788
1821
  "state restoration receipt is captured"
1789
1822
  ],
1790
1823
  does_not_prove: [
@@ -2316,10 +2349,142 @@ function instantiateRiddleProofProfile(profileName, options = {}) {
2316
2349
  }
2317
2350
  );
2318
2351
  }
2352
+
2353
+ // src/humanReviewPacket.ts
2354
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2355
+ var asRecord = (value) => isRecord2(value) ? value : null;
2356
+ var asArray = (value) => Array.isArray(value) ? value : [];
2357
+ var getPath = (value, path) => {
2358
+ let cursor = value;
2359
+ for (const part of path.split(".")) {
2360
+ if (!isRecord2(cursor)) return void 0;
2361
+ cursor = cursor[part];
2362
+ }
2363
+ return cursor;
2364
+ };
2365
+ var formatValue = (value) => {
2366
+ if (value === null || value === void 0 || value === "") return "not captured";
2367
+ if (typeof value === "number") return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(4)));
2368
+ if (typeof value === "boolean") return value ? "true" : "false";
2369
+ return String(value);
2370
+ };
2371
+ var formatCodeValue = (value) => `\`${formatValue(value)}\``;
2372
+ var formatAction = (action) => {
2373
+ const record = asRecord(action);
2374
+ if (!record) return "not captured";
2375
+ const type = record.type ?? "set_mixer_level";
2376
+ const track = record.track ?? "track";
2377
+ const from = formatValue(record.from);
2378
+ const to = formatValue(record.to);
2379
+ const delta = record.delta === null || record.delta === void 0 ? "" : ` (${formatValue(record.delta)})`;
2380
+ return `${type} ${track}: ${from} -> ${to}${delta}`;
2381
+ };
2382
+ var addOptionalList = (lines, heading, values) => {
2383
+ const entries = asArray(values).filter((entry) => entry !== null && entry !== void 0 && entry !== "");
2384
+ if (!entries.length) return;
2385
+ lines.push("", `## ${heading}`, "");
2386
+ for (const entry of entries) lines.push(`- ${formatValue(entry)}`);
2387
+ };
2388
+ function findHumanReviewPacket(value) {
2389
+ if (!value || typeof value !== "object") return null;
2390
+ if (isRecord2(value) && value.kind === "human_review_packet") return value;
2391
+ if (Array.isArray(value)) {
2392
+ for (const entry of value) {
2393
+ const packet = findHumanReviewPacket(entry);
2394
+ if (packet) return packet;
2395
+ }
2396
+ return null;
2397
+ }
2398
+ for (const entry of Object.values(value)) {
2399
+ const packet = findHumanReviewPacket(entry);
2400
+ if (packet) return packet;
2401
+ }
2402
+ return null;
2403
+ }
2404
+ function requireHumanReviewPacket(value) {
2405
+ const packet = findHumanReviewPacket(value);
2406
+ if (!packet) throw new Error("No human_review_packet found");
2407
+ return packet;
2408
+ }
2409
+ function formatHumanReviewPacketMarkdown(packet, options = {}) {
2410
+ if (packet.kind !== "human_review_packet") {
2411
+ throw new Error("Expected a human_review_packet");
2412
+ }
2413
+ const recommendation = asRecord(packet.recommendation) ?? {};
2414
+ const candidate = asRecord(recommendation.candidate) ?? {};
2415
+ const guardrails = asRecord(packet.guardrails) ?? {};
2416
+ const ranking = asRecord(packet.ranking) ?? {};
2417
+ const request = asRecord(packet.request) ?? {};
2418
+ const supportedCandidates = asArray(packet.supportedCandidates);
2419
+ const rejectedCandidates = asArray(packet.rejectedCandidates);
2420
+ const selectedSong = getPath(packet, "target.selectedSong.selectedSong") ?? getPath(packet, "target.routeState.selectedSong");
2421
+ const lines = [
2422
+ `# ${options.title ?? "Human Review Packet"}`,
2423
+ "",
2424
+ `- status: ${formatCodeValue(packet.status)}`,
2425
+ `- domain: ${formatCodeValue(packet.domain)}`,
2426
+ `- evidence_role_pattern: ${formatCodeValue(packet.evidenceRolePattern)}`,
2427
+ `- requested_intent: ${formatValue(packet.requestedIntent)}`,
2428
+ `- selected_song: ${formatValue(selectedSong)}`,
2429
+ "",
2430
+ "## Recommendation",
2431
+ "",
2432
+ `- action: ${formatCodeValue(recommendation.action)}`,
2433
+ `- candidate: ${formatCodeValue(candidate.label)}`,
2434
+ `- candidate_action: ${formatCodeValue(formatAction(candidate.action))}`,
2435
+ `- reason: ${formatValue(recommendation.reason)}`,
2436
+ "",
2437
+ "## Objective Receipts",
2438
+ "",
2439
+ `- supported_candidates: ${formatCodeValue(guardrails.supportedClaimCandidateCount ?? supportedCandidates.length)}`,
2440
+ `- rejected_candidates: ${formatCodeValue(guardrails.rejectedCandidateCount ?? rejectedCandidates.length)}`,
2441
+ `- state_restored_after_loop: ${formatCodeValue(guardrails.stateRestoredAfterLoop)}`,
2442
+ `- candidate_actions_are_transient: ${formatCodeValue(request.candidateActionsAreTransient)}`,
2443
+ `- no_permanent_edit_unless_apply_best: ${formatCodeValue(guardrails.noPermanentEditUnlessApplyBest)}`,
2444
+ "",
2445
+ "## Ranking",
2446
+ "",
2447
+ `- metric: ${formatCodeValue(ranking.metric)}`,
2448
+ `- role: ${formatCodeValue(ranking.role)}`,
2449
+ `- lower_is_better: ${formatCodeValue(ranking.lowerIsBetter)}`,
2450
+ `- baseline: ${formatCodeValue(ranking.baselineCandidateRankingMetric)}`,
2451
+ `- best: ${formatCodeValue(ranking.bestCandidateRankingMetric)}`,
2452
+ `- delta: ${formatCodeValue(ranking.rankingMetricDelta)}`,
2453
+ "",
2454
+ "## Boundary",
2455
+ "",
2456
+ formatValue(packet.proofBoundary)
2457
+ ];
2458
+ addOptionalList(lines, "Listening Prompts", packet.listenerPrompts);
2459
+ addOptionalList(lines, "Caveats", packet.caveats);
2460
+ if (rejectedCandidates.length) {
2461
+ lines.push("", "## Rejected Candidates", "");
2462
+ for (const entry of rejectedCandidates) {
2463
+ const rejected = asRecord(entry) ?? {};
2464
+ const failedReceipts = asArray(rejected.failedReceipts).map(formatValue).join(", ") || "not captured";
2465
+ lines.push(`- ${formatCodeValue(rejected.label)}: ${failedReceipts}`);
2466
+ }
2467
+ }
2468
+ return `${lines.join("\n")}
2469
+ `;
2470
+ }
2471
+ function createHumanReviewPacketArtifacts(proofOrPacket, options = {}) {
2472
+ const packet = requireHumanReviewPacket(proofOrPacket);
2473
+ const markdown = formatHumanReviewPacketMarkdown(packet, options);
2474
+ return {
2475
+ packet,
2476
+ json: `${JSON.stringify(packet, null, 2)}
2477
+ `,
2478
+ markdown
2479
+ };
2480
+ }
2319
2481
  // Annotate the CommonJS export names for ESM import in node:
2320
2482
  0 && (module.exports = {
2321
2483
  RIDDLE_PROOF_PACK_MANIFEST,
2322
2484
  RIDDLE_PROOF_PACK_PROFILES,
2485
+ createHumanReviewPacketArtifacts,
2486
+ findHumanReviewPacket,
2487
+ formatHumanReviewPacketMarkdown,
2323
2488
  getPackEnabledRiddleProofPackProfiles,
2324
2489
  getRiddleProofPackProfile,
2325
2490
  getRiddleProofPackProfileByPackId,
@@ -2327,5 +2492,6 @@ function instantiateRiddleProofProfile(profileName, options = {}) {
2327
2492
  getRiddleProofProfilesByPackId,
2328
2493
  instantiateRiddleProofProfile,
2329
2494
  listRiddleProofPackProfiles,
2330
- listRiddleProofPacks
2495
+ listRiddleProofPacks,
2496
+ requireHumanReviewPacket
2331
2497
  });
package/dist/index.d.cts CHANGED
@@ -59,4 +59,20 @@ interface RiddleProofPackProfileOverrides {
59
59
  */
60
60
  declare function instantiateRiddleProofProfile(profileName: string, options?: RiddleProofPackProfileOverrides): RiddleProofProfile;
61
61
 
62
- export { RIDDLE_PROOF_PACK_MANIFEST, RIDDLE_PROOF_PACK_PROFILES, type RiddleProofPackProfileManifest, getPackEnabledRiddleProofPackProfiles, getRiddleProofPackProfile, getRiddleProofPackProfileByPackId, getRiddleProofPackProfileManifest, getRiddleProofProfilesByPackId, instantiateRiddleProofProfile, listRiddleProofPackProfiles, listRiddleProofPacks };
62
+ type HumanReviewPacket = Record<string, unknown> & {
63
+ kind: "human_review_packet";
64
+ };
65
+ interface HumanReviewPacketMarkdownOptions {
66
+ title?: string;
67
+ }
68
+ interface HumanReviewPacketArtifacts {
69
+ packet: HumanReviewPacket;
70
+ json: string;
71
+ markdown: string;
72
+ }
73
+ declare function findHumanReviewPacket(value: unknown): HumanReviewPacket | null;
74
+ declare function requireHumanReviewPacket(value: unknown): HumanReviewPacket;
75
+ declare function formatHumanReviewPacketMarkdown(packet: HumanReviewPacket, options?: HumanReviewPacketMarkdownOptions): string;
76
+ declare function createHumanReviewPacketArtifacts(proofOrPacket: unknown, options?: HumanReviewPacketMarkdownOptions): HumanReviewPacketArtifacts;
77
+
78
+ export { type HumanReviewPacket, type HumanReviewPacketArtifacts, type HumanReviewPacketMarkdownOptions, RIDDLE_PROOF_PACK_MANIFEST, RIDDLE_PROOF_PACK_PROFILES, type RiddleProofPackProfileManifest, createHumanReviewPacketArtifacts, findHumanReviewPacket, formatHumanReviewPacketMarkdown, getPackEnabledRiddleProofPackProfiles, getRiddleProofPackProfile, getRiddleProofPackProfileByPackId, getRiddleProofPackProfileManifest, getRiddleProofProfilesByPackId, instantiateRiddleProofProfile, listRiddleProofPackProfiles, listRiddleProofPacks, requireHumanReviewPacket };
package/dist/index.d.ts CHANGED
@@ -59,4 +59,20 @@ interface RiddleProofPackProfileOverrides {
59
59
  */
60
60
  declare function instantiateRiddleProofProfile(profileName: string, options?: RiddleProofPackProfileOverrides): RiddleProofProfile;
61
61
 
62
- export { RIDDLE_PROOF_PACK_MANIFEST, RIDDLE_PROOF_PACK_PROFILES, type RiddleProofPackProfileManifest, getPackEnabledRiddleProofPackProfiles, getRiddleProofPackProfile, getRiddleProofPackProfileByPackId, getRiddleProofPackProfileManifest, getRiddleProofProfilesByPackId, instantiateRiddleProofProfile, listRiddleProofPackProfiles, listRiddleProofPacks };
62
+ type HumanReviewPacket = Record<string, unknown> & {
63
+ kind: "human_review_packet";
64
+ };
65
+ interface HumanReviewPacketMarkdownOptions {
66
+ title?: string;
67
+ }
68
+ interface HumanReviewPacketArtifacts {
69
+ packet: HumanReviewPacket;
70
+ json: string;
71
+ markdown: string;
72
+ }
73
+ declare function findHumanReviewPacket(value: unknown): HumanReviewPacket | null;
74
+ declare function requireHumanReviewPacket(value: unknown): HumanReviewPacket;
75
+ declare function formatHumanReviewPacketMarkdown(packet: HumanReviewPacket, options?: HumanReviewPacketMarkdownOptions): string;
76
+ declare function createHumanReviewPacketArtifacts(proofOrPacket: unknown, options?: HumanReviewPacketMarkdownOptions): HumanReviewPacketArtifacts;
77
+
78
+ export { type HumanReviewPacket, type HumanReviewPacketArtifacts, type HumanReviewPacketMarkdownOptions, RIDDLE_PROOF_PACK_MANIFEST, RIDDLE_PROOF_PACK_PROFILES, type RiddleProofPackProfileManifest, createHumanReviewPacketArtifacts, findHumanReviewPacket, formatHumanReviewPacketMarkdown, getPackEnabledRiddleProofPackProfiles, getRiddleProofPackProfile, getRiddleProofPackProfileByPackId, getRiddleProofPackProfileManifest, getRiddleProofProfilesByPackId, instantiateRiddleProofProfile, listRiddleProofPackProfiles, listRiddleProofPacks, requireHumanReviewPacket };
package/dist/index.js CHANGED
@@ -1660,6 +1660,7 @@ var ratchet_loop_mix_level_search_default = {
1660
1660
  path: "__NEON_MIX_PROOF__.runRatchetLoop",
1661
1661
  args: [
1662
1662
  {
1663
+ intent: "turn the chord part down a little",
1663
1664
  strategy: "mix-level-search",
1664
1665
  focusTracks: [
1665
1666
  "bass",
@@ -1694,6 +1695,15 @@ var ratchet_loop_mix_level_search_default = {
1694
1695
  },
1695
1696
  {
1696
1697
  path: "supportedClaimCandidateCount"
1698
+ },
1699
+ {
1700
+ path: "humanReviewPacket.status"
1701
+ },
1702
+ {
1703
+ path: "humanReviewPacket.recommendation.candidate.action.track"
1704
+ },
1705
+ {
1706
+ path: "humanReviewPacket.ranking.role"
1697
1707
  }
1698
1708
  ]
1699
1709
  },
@@ -1703,6 +1713,24 @@ var ratchet_loop_mix_level_search_default = {
1703
1713
  expected_value: true,
1704
1714
  timeout_ms: 1e4
1705
1715
  },
1716
+ {
1717
+ type: "assert_window_value",
1718
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.kind",
1719
+ expected_value: "human_review_packet",
1720
+ timeout_ms: 1e4
1721
+ },
1722
+ {
1723
+ type: "assert_window_value",
1724
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.ranking.role",
1725
+ expected_value: "review_order_only",
1726
+ timeout_ms: 1e4
1727
+ },
1728
+ {
1729
+ type: "assert_window_value",
1730
+ path: "__neonMixProof.ratchetLoop.humanReviewPacket.request.candidateActionsAreTransient",
1731
+ expected_value: true,
1732
+ timeout_ms: 1e4
1733
+ },
1706
1734
  {
1707
1735
  type: "screenshot",
1708
1736
  label: "neon-ratchet-loop-mix-level-search",
@@ -1750,6 +1778,7 @@ var ratchet_loop_mix_level_search_default = {
1750
1778
  "claim candidates are captured",
1751
1779
  "each candidate records claim receipts",
1752
1780
  "best supported claim candidate or human-review status is captured",
1781
+ "compact human-review packet is captured",
1753
1782
  "state restoration receipt is captured"
1754
1783
  ],
1755
1784
  does_not_prove: [
@@ -2281,9 +2310,141 @@ function instantiateRiddleProofProfile(profileName, options = {}) {
2281
2310
  }
2282
2311
  );
2283
2312
  }
2313
+
2314
+ // src/humanReviewPacket.ts
2315
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2316
+ var asRecord = (value) => isRecord2(value) ? value : null;
2317
+ var asArray = (value) => Array.isArray(value) ? value : [];
2318
+ var getPath = (value, path) => {
2319
+ let cursor = value;
2320
+ for (const part of path.split(".")) {
2321
+ if (!isRecord2(cursor)) return void 0;
2322
+ cursor = cursor[part];
2323
+ }
2324
+ return cursor;
2325
+ };
2326
+ var formatValue = (value) => {
2327
+ if (value === null || value === void 0 || value === "") return "not captured";
2328
+ if (typeof value === "number") return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(4)));
2329
+ if (typeof value === "boolean") return value ? "true" : "false";
2330
+ return String(value);
2331
+ };
2332
+ var formatCodeValue = (value) => `\`${formatValue(value)}\``;
2333
+ var formatAction = (action) => {
2334
+ const record = asRecord(action);
2335
+ if (!record) return "not captured";
2336
+ const type = record.type ?? "set_mixer_level";
2337
+ const track = record.track ?? "track";
2338
+ const from = formatValue(record.from);
2339
+ const to = formatValue(record.to);
2340
+ const delta = record.delta === null || record.delta === void 0 ? "" : ` (${formatValue(record.delta)})`;
2341
+ return `${type} ${track}: ${from} -> ${to}${delta}`;
2342
+ };
2343
+ var addOptionalList = (lines, heading, values) => {
2344
+ const entries = asArray(values).filter((entry) => entry !== null && entry !== void 0 && entry !== "");
2345
+ if (!entries.length) return;
2346
+ lines.push("", `## ${heading}`, "");
2347
+ for (const entry of entries) lines.push(`- ${formatValue(entry)}`);
2348
+ };
2349
+ function findHumanReviewPacket(value) {
2350
+ if (!value || typeof value !== "object") return null;
2351
+ if (isRecord2(value) && value.kind === "human_review_packet") return value;
2352
+ if (Array.isArray(value)) {
2353
+ for (const entry of value) {
2354
+ const packet = findHumanReviewPacket(entry);
2355
+ if (packet) return packet;
2356
+ }
2357
+ return null;
2358
+ }
2359
+ for (const entry of Object.values(value)) {
2360
+ const packet = findHumanReviewPacket(entry);
2361
+ if (packet) return packet;
2362
+ }
2363
+ return null;
2364
+ }
2365
+ function requireHumanReviewPacket(value) {
2366
+ const packet = findHumanReviewPacket(value);
2367
+ if (!packet) throw new Error("No human_review_packet found");
2368
+ return packet;
2369
+ }
2370
+ function formatHumanReviewPacketMarkdown(packet, options = {}) {
2371
+ if (packet.kind !== "human_review_packet") {
2372
+ throw new Error("Expected a human_review_packet");
2373
+ }
2374
+ const recommendation = asRecord(packet.recommendation) ?? {};
2375
+ const candidate = asRecord(recommendation.candidate) ?? {};
2376
+ const guardrails = asRecord(packet.guardrails) ?? {};
2377
+ const ranking = asRecord(packet.ranking) ?? {};
2378
+ const request = asRecord(packet.request) ?? {};
2379
+ const supportedCandidates = asArray(packet.supportedCandidates);
2380
+ const rejectedCandidates = asArray(packet.rejectedCandidates);
2381
+ const selectedSong = getPath(packet, "target.selectedSong.selectedSong") ?? getPath(packet, "target.routeState.selectedSong");
2382
+ const lines = [
2383
+ `# ${options.title ?? "Human Review Packet"}`,
2384
+ "",
2385
+ `- status: ${formatCodeValue(packet.status)}`,
2386
+ `- domain: ${formatCodeValue(packet.domain)}`,
2387
+ `- evidence_role_pattern: ${formatCodeValue(packet.evidenceRolePattern)}`,
2388
+ `- requested_intent: ${formatValue(packet.requestedIntent)}`,
2389
+ `- selected_song: ${formatValue(selectedSong)}`,
2390
+ "",
2391
+ "## Recommendation",
2392
+ "",
2393
+ `- action: ${formatCodeValue(recommendation.action)}`,
2394
+ `- candidate: ${formatCodeValue(candidate.label)}`,
2395
+ `- candidate_action: ${formatCodeValue(formatAction(candidate.action))}`,
2396
+ `- reason: ${formatValue(recommendation.reason)}`,
2397
+ "",
2398
+ "## Objective Receipts",
2399
+ "",
2400
+ `- supported_candidates: ${formatCodeValue(guardrails.supportedClaimCandidateCount ?? supportedCandidates.length)}`,
2401
+ `- rejected_candidates: ${formatCodeValue(guardrails.rejectedCandidateCount ?? rejectedCandidates.length)}`,
2402
+ `- state_restored_after_loop: ${formatCodeValue(guardrails.stateRestoredAfterLoop)}`,
2403
+ `- candidate_actions_are_transient: ${formatCodeValue(request.candidateActionsAreTransient)}`,
2404
+ `- no_permanent_edit_unless_apply_best: ${formatCodeValue(guardrails.noPermanentEditUnlessApplyBest)}`,
2405
+ "",
2406
+ "## Ranking",
2407
+ "",
2408
+ `- metric: ${formatCodeValue(ranking.metric)}`,
2409
+ `- role: ${formatCodeValue(ranking.role)}`,
2410
+ `- lower_is_better: ${formatCodeValue(ranking.lowerIsBetter)}`,
2411
+ `- baseline: ${formatCodeValue(ranking.baselineCandidateRankingMetric)}`,
2412
+ `- best: ${formatCodeValue(ranking.bestCandidateRankingMetric)}`,
2413
+ `- delta: ${formatCodeValue(ranking.rankingMetricDelta)}`,
2414
+ "",
2415
+ "## Boundary",
2416
+ "",
2417
+ formatValue(packet.proofBoundary)
2418
+ ];
2419
+ addOptionalList(lines, "Listening Prompts", packet.listenerPrompts);
2420
+ addOptionalList(lines, "Caveats", packet.caveats);
2421
+ if (rejectedCandidates.length) {
2422
+ lines.push("", "## Rejected Candidates", "");
2423
+ for (const entry of rejectedCandidates) {
2424
+ const rejected = asRecord(entry) ?? {};
2425
+ const failedReceipts = asArray(rejected.failedReceipts).map(formatValue).join(", ") || "not captured";
2426
+ lines.push(`- ${formatCodeValue(rejected.label)}: ${failedReceipts}`);
2427
+ }
2428
+ }
2429
+ return `${lines.join("\n")}
2430
+ `;
2431
+ }
2432
+ function createHumanReviewPacketArtifacts(proofOrPacket, options = {}) {
2433
+ const packet = requireHumanReviewPacket(proofOrPacket);
2434
+ const markdown = formatHumanReviewPacketMarkdown(packet, options);
2435
+ return {
2436
+ packet,
2437
+ json: `${JSON.stringify(packet, null, 2)}
2438
+ `,
2439
+ markdown
2440
+ };
2441
+ }
2284
2442
  export {
2285
2443
  RIDDLE_PROOF_PACK_MANIFEST,
2286
2444
  RIDDLE_PROOF_PACK_PROFILES,
2445
+ createHumanReviewPacketArtifacts,
2446
+ findHumanReviewPacket,
2447
+ formatHumanReviewPacketMarkdown,
2287
2448
  getPackEnabledRiddleProofPackProfiles,
2288
2449
  getRiddleProofPackProfile,
2289
2450
  getRiddleProofPackProfileByPackId,
@@ -2291,5 +2452,6 @@ export {
2291
2452
  getRiddleProofProfilesByPackId,
2292
2453
  instantiateRiddleProofProfile,
2293
2454
  listRiddleProofPackProfiles,
2294
- listRiddleProofPacks
2455
+ listRiddleProofPacks,
2456
+ requireHumanReviewPacket
2295
2457
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof-packs",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Reusable proof pack profiles and metadata helpers for the Riddle proof framework.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -33,7 +33,7 @@ This pack is the first app-specific lab for the open Riddle Proof architecture.
33
33
 
34
34
  The loop is not mix-specific as a proof concept. The proof concept is a bounded ratchet loop: propose a candidate, apply it, collect evidence, classify the result, restore or keep state, and repeat until the budget is exhausted.
35
35
 
36
- This pack's first concrete strategy is `mix-level-search`, which turns small level edits into change-claim candidates. Each candidate says what action will be attempted, what receipts must support the claim, and what evidence should be reviewed afterward. The loop may include a ranking metric to order review, but the verdict comes from receipts and invariants, not from a universal mix-quality number. It still does not decide subjective mix taste; the output is a listening and review packet.
36
+ This pack's first concrete strategy is `mix-level-search`, which turns small level edits into change-claim candidates. Each candidate says what action will be attempted, what receipts must support the claim, and what evidence should be reviewed afterward. The loop may include a ranking metric to order review, but the verdict comes from receipts and invariants, not from a universal mix-quality number. It still does not decide subjective mix taste; the output is a `humanReviewPacket` for listening handoff.
37
37
 
38
38
  ## Example evidence
39
39
 
@@ -44,6 +44,7 @@ The `examples/` directory contains local Playwright proof results captured again
44
44
  - `run-003-full-matrix`: passing `current_target` viewport matrix across desktop, phone, iPad Mini, and iPad with `0 px` horizontal overflow.
45
45
  - `run-004-ratchet-loop-mix-level-search`: passing `interaction_snapshots` proof where a bounded loop tested six mix-level change-claim candidates, found a supported `chord -0.10` candidate, recorded receipt-level verdicts, and restored app state without keeping the edit.
46
46
  - `run-005-explore-songs-and-mixes-final`: passing `current_target` exploration sweep across four songs and eight song/part entries, with `8` passing entries, `0` prioritized findings, and no clipping after the local app-contract and mix-headroom ratchet.
47
+ - `run-006-ratchet-loop-human-review-packet`: passing `interaction_snapshots` proof where the same bounded loop returned a compact `humanReviewPacket` with the recommended `chord -0.10` candidate, objective guardrails, `review_order_only` ranking, state restoration, and explicit listening caveats.
47
48
 
48
49
  ## Naming note
49
50
 
@@ -112,6 +112,17 @@ Pack summary guidance first; Riddle Proof core only if a general display primiti
112
112
  - rerun: passed on May 24, 2026 with `8` entries, `8` passed, and `0` findings.
113
113
  - next sharper question: can this exploration workflow become the normal local pack/profile loop before any changeset or npm release?
114
114
 
115
+ ### Run 006 made the listening handoff compact
116
+
117
+ - run: `run-006-ratchet-loop-human-review-packet`
118
+ - claim: a bounded ratchet loop can return a compact human-review packet that summarizes supported candidates, objective guardrails, state restoration, ranking-as-review-order, and listening caveats.
119
+ - observed evidence: loop status was `claim_candidate_supported`; the packet kind was `human_review_packet`; packet status was `candidate_ready_for_listening_review`; the recommended candidate was `chord -0.10`; supported candidate count was `6`; rejected candidate count was `0`; ranking role was `review_order_only`; app state was restored after the loop; permanent edit was not kept.
120
+ - classification: none; passing `interaction_snapshots` proof with subjective listening caveat.
121
+ - smallest layer changed: app proof contract and proof-pack profile assertions.
122
+ - change made: added `humanReviewPacket` to the Neon ratchet-loop result and updated the pack profile to assert the packet shape.
123
+ - rerun: passed on May 24, 2026 with local Playwright.
124
+ - next sharper question: can the packet become the standard output shape for one-off and background candidate operators across more than `mix-level-search`?
125
+
115
126
  ### Local runner shutdown needs a small ergonomics follow-up
116
127
 
117
128
  - run: `run-002-mix-change`, `run-003-full-matrix`, `run-004-ratchet-loop-mix-level-search`
@@ -35,7 +35,8 @@ The project shows that a complex audio app can improve proof confidence mostly b
35
35
  - Run 003: a `current_target` matrix passed across desktop, phone, iPad Mini, and iPad with `0 px` horizontal overflow.
36
36
  - Run 004: a bounded loop tested six `mix-level-search` change-claim candidates, returned `chord -0.10` as a supported review candidate with receipt-level evidence, and restored app state.
37
37
  - Run 005: a `current_target` exploration sweep sampled four songs and eight song/part entries. The first real sweep exposed app-contract normalization gaps and hot built-in song presets; after the local ratchet fixes it passed with `8` entries, `0` findings, and no clipping.
38
+ - Run 006: the bounded loop returned a compact `humanReviewPacket` with the supported `chord -0.10` candidate, objective guardrails, state restoration, review-order ranking, and listening caveats.
38
39
 
39
40
  ## Honest boundary
40
41
 
41
- These runs prove objective claims about a running app target. They do not prove that the mix is tasteful, that every song section is healthy, or that a release candidate is better than production. Run 005 is still bounded to configured song/part limits. The ratchet loop is a generic proof-loop shape; `mix-level-search` is only the first Neon strategy plugged into it.
42
+ These runs prove objective claims about a running app target. They do not prove that the mix is tasteful, that every song section is healthy, or that a release candidate is better than production. Run 005 is still bounded to configured song/part limits. Run 006 makes the human handoff explicit: the packet is a compact review object, not an automated taste verdict. The ratchet loop is a generic proof-loop shape; `mix-level-search` is only the first Neon strategy plugged into it.
@@ -282,6 +282,61 @@ Next sharper question:
282
282
 
283
283
  Can the same exploration shape become a reusable pack/profile workflow where a user can choose a bounded target set, run locally during iteration, and publish only after the evidence is worth sharing?
284
284
 
285
+ ## Run 006 - Human-review packet handoff
286
+
287
+ Claim:
288
+
289
+ Neon can turn the bounded `mix-level-search` loop into a compact handoff object that a human or follow-on agent can review without reading the full proof JSON.
290
+
291
+ Profile:
292
+
293
+ `profiles/ratchet-loop-mix-level-search.json`
294
+
295
+ Evidence to capture:
296
+
297
+ - loop status and supported candidate count
298
+ - recommended candidate action
299
+ - per-candidate objective guardrail summary
300
+ - state restoration receipt
301
+ - ranking role as review order only
302
+ - listening-review caveats
303
+
304
+ Possible outcomes:
305
+
306
+ - `claim_candidate_supported`: at least one candidate has the receipts needed to support its change claim and the packet can recommend it for listening review.
307
+ - `needs_human_review`: evidence is valid but no candidate satisfies every objective receipt.
308
+ - `proof_insufficient`: the app contract does not expose a packet or enough candidate receipts.
309
+ - `profile_calibration`: the packet exists but points to the wrong window, target, or candidate set.
310
+
311
+ Observed status:
312
+
313
+ Passed on May 24, 2026 with `local-playwright`.
314
+
315
+ Observed evidence:
316
+
317
+ - loop status `claim_candidate_supported`
318
+ - packet kind `human_review_packet`
319
+ - packet status `candidate_ready_for_listening_review`
320
+ - recommended candidate `chord -0.10`
321
+ - recommended action `set_mixer_level chord 0.38 -> 0.28`
322
+ - supported candidates `6`
323
+ - rejected candidates `0`
324
+ - ranking role `review_order_only`
325
+ - state restored after loop `true`
326
+ - permanent edit kept `false`
327
+
328
+ Failure classification:
329
+
330
+ None. This was a passing `interaction_snapshots` proof with an explicit listening-review caveat.
331
+
332
+ Smallest layer changed:
333
+
334
+ App proof contract and proof-pack profile. Riddle Proof core did not need a change.
335
+
336
+ Next sharper question:
337
+
338
+ Can one-off commands and background runs use this packet as their common output surface while strategy-specific code remains behind the app contract?
339
+
285
340
  ## Project note
286
341
 
287
342
  The ratchet is not a pass. The ratchet is the next sharper question.
@@ -22,6 +22,10 @@ Large metrics belong in artifacts. The summary should answer:
22
22
  - did the intended metric move?
23
23
  - what should a human review next?
24
24
 
25
+ ## Handoff packets beat raw JSON spelunking
26
+
27
+ The full proof result should remain auditable, but one-off and background loops need a compact handoff object. A good `humanReviewPacket` lists supported and rejected candidates, objective guardrails, restoration status, review-order ranking, and caveats that separate proof from taste.
28
+
25
29
  ## Core changes are last
26
30
 
27
31
  Most ratchet steps should change profile JSON, pack docs, app proof contracts, or app fixtures. Riddle Proof core changes are justified only when the missing primitive applies beyond Neon.