Implement HARP and CPL milestones through v0.5.11

This commit is contained in:
2026-07-20 07:39:50 -07:00
parent 4b8dc903e4
commit 550f2cbff9
109 changed files with 57576 additions and 741 deletions
+60
View File
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("routes both manuscript surfaces through instrumented TipTap transactions", async () => {
const frontend = await source("../src/Thinkloom.tsx");
assert.match(frontend, /useEditor\(\{[\s\S]*onUpdate:/);
assert.match(frontend, /apply_composition_command/);
assert.match(frontend, /label="Final manuscript editor"/);
assert.doesNotMatch(frontend, /finalEditor|<textarea[^>]+aria-label="Final manuscript"/);
assert.match(frontend, /onPasteCapture[\s\S]*origin: "imported_or_pasted"/);
assert.doesNotMatch(frontend, /onPasteCapture[\s\S]{0,180}recorded_direct_human_input/);
});
test("captures every required coalescing boundary without originality heuristics", async () => {
const [frontend, composition] = await Promise.all([
source("../src/Thinkloom.tsx"),
source("../src-tauri/src/provenance/composition.rs"),
]);
for (const boundary of [
"idle", "focus_loss", "section_change", "ai_operation", "checkpoint",
"phase_change", "explicit_save", "document_close",
]) assert.match(`${frontend}\n${composition}`, new RegExp(`\\b${boundary}\\b`));
for (const origin of [
"recorded_direct_human_input", "human_expressive_input_via_transcription",
"accepted_ai_output", "imported_or_pasted", "system_restoration", "unattested",
]) assert.match(`${frontend}\n${composition}`, new RegExp(origin));
assert.doesNotMatch(composition, /edit[_ -]?count|elapsed[_ -]?time|word[_ -]?count|retained[_ -]?word[_ -]?ratio/i);
});
test("binds partial AI dispositions and deterministically replays surviving-span lineage", async () => {
const [frontend, composition, native, writer] = await Promise.all([
source("../src/Thinkloom.tsx"),
source("../src-tauri/src/provenance/composition.rs"),
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/writer.rs"),
]);
assert.match(frontend, /invocationId: project\.generation\.id/);
assert.match(frontend, /acceptedRanges: \[\{ start: 0, end: acceptedEnd \}\]/);
assert.match(frontend, /rejectedRanges: acceptedEnd < responseEnd/);
assert.match(composition, /ai-acceptance-disposition/);
assert.match(composition, /result_revision_id/);
assert.match(composition, /record_type == "composition-command"/);
assert.match(composition, /lineage_reference_ids/);
assert.match(composition, /RecordedOrigin::Unattested/);
assert.match(writer, /rebuild_projection_cache/);
assert.match(native, /manuscript\/manuscript\.md/);
assert.match(composition, /replays_manual_paste_ai_revision_and_restoration_with_lineage/);
assert.match(composition, /unicode_scalar_diff_and_partial_ai_ranges_are_exact/);
assert.match(composition, /refuses_a_stale_preimage_without_committing_an_event/);
assert.match(composition, /concurrent_edits_against_one_preimage_commit_exactly_once/);
assert.match(composition, /retries_are_idempotent_and_action_id_reuse_conflicts/);
});
+61
View File
@@ -0,0 +1,61 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("freezes contribution maps from native composition revisions and exact deposits", async () => {
const [native, map, modules] = await Promise.all([
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/contribution_map.rs"),
source("../src-tauri/src/provenance/mod.rs"),
]);
assert.match(modules, /pub mod contribution_map;/);
assert.match(native, /fn freeze_contribution_map/);
assert.match(native, /fn load_contribution_map/);
assert.match(native, /contribution_map::freeze_current/);
assert.match(native, /"deposits"/);
assert.match(map, /record_type: "deposit-snapshot"/);
assert.match(map, /record_type: "contribution-map"/);
assert.match(map, /record_type: "contribution-map-bundle"/);
assert.match(map, /deposit_sha256/);
assert.match(map, /manuscript_revision_id/);
});
test("provides complete scalar coverage with deterministic structural locators and ancestry", async () => {
const map = await source("../src-tauri/src/provenance/contribution_map.rs");
assert.match(map, /coordinate_system: "unicode_scalar"/);
assert.match(map, /validate_source_coverage/);
assert.match(map, /validate_map_coverage/);
assert.match(map, /merge_source_spans/);
assert.match(map, /ancestry_segment_id\.clone\(\)/);
assert.match(map, /chapter: Some/);
assert.match(map, /paragraph: Some/);
assert.match(map, /page: Some/);
assert.doesNotMatch(map, /locale|Collator|toLocaleString/);
assert.match(map, /canonical_map_bytes/);
assert.match(map, /identical_canonical_input_is_byte_identical_for_any_span_order/);
assert.match(map, /equivalent_adjacent_source_splits_merge_to_the_same_map/);
});
test("separates assertions from origin and exposes every evidence boundary", async () => {
const [map, schema] = await Promise.all([
source("../src-tauri/src/provenance/contribution_map.rs"),
source("../schemas/provenance/v1/contribution-map.schema.json"),
]);
for (const predicate of ["included_in_deposit", "selected_by_human", "arranged_by_human"])
assert.match(map, new RegExp(predicate));
assert.match(map, /assertion_evaluations/);
for (const status of ["stale", "degraded", "unverified", "unattested"])
assert.match(map, new RegExp(`"${status}"`));
assert.match(map, /not a human-authorship percentage/);
assert.match(schema, /denominator_unit/);
assert.match(schema, /denominator_definition/);
assert.match(map, /frozen_map_becomes_stale_after_a_later_composition_revision/);
assert.match(map, /verified_frozen_map_is_exact_and_reused_for_identical_input/);
assert.match(map, /missing_frozen_deposit_is_visibly_degraded/);
assert.match(map, /inconclusive_source_verification_is_visibly_unverified/);
});
+71
View File
@@ -0,0 +1,71 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("creates the six milestone 10 artifacts through the native HARP export service", async () => {
const [native, exporter, modules, ui] = await Promise.all([
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/export.rs"),
source("../src-tauri/src/provenance/mod.rs"),
source("../src/HarpExportPanel.tsx"),
]);
assert.match(modules, /pub mod export;/);
assert.match(native, /fn export_harp_artifacts/);
assert.match(native, /fn verify_harp_sanitized_archive/);
assert.match(ui, /export_harp_artifacts/);
assert.match(ui, /verify_harp_sanitized_archive/);
for (const role of [
"registration_worksheet",
"human_readable_harp",
"machine_readable_harp",
"deposit_copy",
"sanitized_supporting_archive",
"full_private_archive",
]) assert.match(exporter, new RegExp(`"${role}"`));
});
test("discloses and hash-binds every required sanitized omission category", async () => {
const [exporter, generator] = await Promise.all([
source("../src-tauri/src/provenance/export.rs"),
source("../scripts/generate-provenance-stage2.mjs"),
]);
for (const category of [
"private_conversation",
"rejected_model_output",
"credential_authorization_material",
"personal_identifier",
"internal_path",
"provider_metadata_not_required",
"protected_source_body",
]) {
assert.match(exporter, new RegExp(`"${category}"`));
assert.match(generator, new RegExp(`"${category}"`));
}
assert.match(exporter, /retained_binding_sha256/);
assert.match(exporter, /disclosure_sha256/);
assert.match(exporter, /rules_sha256/);
assert.match(exporter, /canonical_digest\(&identity\)/);
});
test("verifies retained files without claiming sanitized completeness", async () => {
const [exporter, ui] = await Promise.all([
source("../src-tauri/src/provenance/export.rs"),
source("../src/HarpExportPanel.tsx"),
]);
assert.match(exporter, /selective_disclosed_subset/);
assert.match(exporter, /verified_selective/);
assert.match(exporter, /intentionally incomplete/);
assert.match(exporter, /sha256_digest\(&bytes\) != binding\["sha256"\]/);
assert.match(exporter, /export must not mutate the CPL chain/);
assert.doesNotMatch(exporter, /CplService::new/);
assert.match(ui, /selective evidence subset/i);
assert.match(ui, /does not claim the omitted private history is present/i);
assert.match(ui, /Full private archive/);
assert.match(ui, /Redact the declared author name/);
});
+77
View File
@@ -0,0 +1,77 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("generates approved deterministic HARP artifacts through the native CPL service", async () => {
const [native, harp, modules] = await Promise.all([
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/harp.rs"),
source("../src-tauri/src/provenance/mod.rs"),
]);
assert.match(modules, /pub mod harp;/);
assert.match(native, /fn generate_harp/);
assert.match(native, /fn load_harp/);
assert.match(native, /harp::generate_current/);
assert.match(harp, /HARP_GENERATION_APPROVED/);
assert.match(harp, /HARP_GENERATED/);
assert.match(harp, /human-authorship-record/);
assert.match(harp, /harp-export-manifest/);
assert.match(harp, /harp-generation-bundle/);
});
test("emits every milestone 8 report with common binding metadata", async () => {
const harp = await source("../src-tauri/src/provenance/harp.rs");
for (const artifact of [
"human-authorship-summary.md",
"final-text-contribution-map.svg",
"representative-transformations.md",
"ai-system-disclosure.md",
"coverage-and-limitations.md",
"registration-language.md",
"harp.json",
"verification-report.json",
"supporting-archive-manifest.json",
]) assert.match(harp, new RegExp(artifact.replaceAll(".", "\\.")));
for (const binding of [
"deposit_sha256",
"manuscript_revision_id",
"cpl_chain_head",
"cpl_event_sequence",
"harp_schema_version",
"harp_generator_version",
"application_version",
"policy_profile_version",
"policy_retrieval_date",
"sanitization_profile",
"legal_scope_statement",
]) assert.match(harp, new RegExp(binding));
});
test("does not use an LLM, a legal classifier, or a human percentage", async () => {
const harp = await source("../src-tauri/src/provenance/harp.rs");
assert.doesNotMatch(harp, /generate_text\(/);
assert.doesNotMatch(harp, /reqwest/);
assert.doesNotMatch(harp, /human_percentage/);
assert.match(harp, /not a human-authorship percentage/);
assert.match(harp, /Copyright Office/);
});
test("binds staleness to manuscript, deposit, policy, assertion, and dependency changes", async () => {
const harp = await source("../src-tauri/src/provenance/harp.rs");
for (const reason of [
"manuscript_revision_changed",
"deposit_digest_changed",
"policy_profile_changed",
"assertion_set_changed",
"dependency_set_changed",
]) assert.match(harp, new RegExp(reason));
assert.match(harp, /fn dependency_changes_are_stale/);
assert.match(harp, /applicability_status.*stale/s);
});
+6 -4
View File
@@ -20,16 +20,18 @@ test("defines a native-only Thinkloom application shell", async () => {
});
test("implements the control and privacy contracts", async () => {
const [source, css] = await Promise.all([
const [source, provenance, css] = await Promise.all([
readFile(new URL("../src/Thinkloom.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/ProvenanceWorkspace.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/globals.css", import.meta.url), "utf8"),
]);
for (const phrase of ["Append to ideas", "Summarize draft", "Lore & context", "Insert at cursor", "Replace selection", "New section", "Discard", "History recorded", "No audio retained", "Approve for this project", "Relationships, not percentages"]) {
for (const phrase of ["Append to ideas", "Summarize draft", "Lore & context", "Insert at cursor", "Replace selection", "New section", "Discard", "History recorded", "No audio retained", "Approve for this project"]) {
assert.match(source, new RegExp(phrase, "i"));
}
assert.match(provenance, /Provenance coverage is not a human-authorship score/);
assert.match(source, /GENERATION_PARTIALLY_ACCEPTED/);
assert.match(source, /CLOUD_PROCESSING_APPROVED/);
assert.match(source, /CLOUD_APPROVAL_CHANGED/);
assert.match(source, /store_provider_secret/);
assert.match(source, /New empty project/);
assert.match(source, /function createEmptyProject/);
@@ -139,7 +141,7 @@ test("externalizes and documents every model prompt", async () => {
}
const version = JSON.parse(packageRaw).version;
assert.equal(version, "0.5.0");
assert.equal(version, "0.5.11");
const packageLock = JSON.parse(packageLockRaw);
assert.equal(packageLock.version, version);
assert.equal(packageLock.packages[""].version, version);
+66
View File
@@ -0,0 +1,66 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const rustModules = [
"canonical",
"identifiers",
"records",
"writer",
"ledger",
"recovery",
"verifier",
"composition",
"assertions",
"projections",
"harp",
"export",
];
test("places all authoritative provenance operations behind the native CPL service", async () => {
const [frontend, lib, moduleSources] = await Promise.all([
readFile(new URL("../src/Thinkloom.tsx", import.meta.url), "utf8"),
readFile(new URL("../src-tauri/src/lib.rs", import.meta.url), "utf8"),
Promise.all(rustModules.map((name) => readFile(new URL(`../src-tauri/src/provenance/${name}.rs`, import.meta.url), "utf8"))),
]);
const native = moduleSources.join("\n");
assert.match(lib, /pub mod provenance/);
assert.match(frontend, /apply_phase1_command/);
assert.doesNotMatch(frontend, /persistNativeState|persist_state/);
assert.match(frontend, /verify_provenance/);
assert.match(frontend, /Native integrity verification/);
assert.doesNotMatch(frontend, /previousHash|manuscriptHash|const hash\s*=|event\.hash/);
assert.doesNotMatch(frontend, /History verified:.*linked events/);
assert.doesNotMatch(frontend, /provenanceChainHead:\s*project/);
for (const phase of ["PREPARED", "RECORDS_DURABLE", "LEDGER_APPENDED", "CHAIN_HEAD_ADVANCED", "SQLITE_APPLIED", "COMPLETE", "QUARANTINED", "FAILED"]) {
assert.match(native, new RegExp(phase));
}
for (const capability of ["client_action_id", "LockFileEx", "event_sequence", "SegmentManifest", "VerificationReport", "canonicalize", "normalize_nfc", "RecoveryClassification"]) {
assert.match(native, new RegExp(capability));
}
});
test("keeps milestone-three crash and concurrency acceptance tests executable", async () => {
const tests = await readFile(new URL("../src-tauri/src/provenance/tests.rs", import.meta.url), "utf8");
for (const boundary of [
"IntentPrepared",
"FirstRecordStaged",
"RecordFlushed",
"RecordMoved",
"RecordDirectorySynced",
"LedgerAppendBeforeFlush",
"LedgerFlushed",
"ChainHeadTemporaryWritten",
"ChainHeadReplaced",
"SegmentManifestFlushed",
"SegmentMoved",
"NewActiveSegmentCreated",
]) {
assert.match(tests, new RegExp(boundary));
}
assert.match(tests, /os_writer_lock_serializes_concurrent_actions/);
assert.match(tests, /retries_are_idempotent_and_conflicts_are_rejected/);
assert.match(tests, /recovery_rebuilds_sqlite_from_authoritative_events/);
});
+61
View File
@@ -0,0 +1,61 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("routes Phase 1 UI state through typed native commands and canonical replay", async () => {
const [frontend, native, phase1] = await Promise.all([
source("../src/Thinkloom.tsx"),
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/phase1.rs"),
]);
assert.match(frontend, /apply_phase1_command/);
assert.match(frontend, /load_phase1_projection/);
assert.doesNotMatch(frontend, /persist_state|load_project_state|application-state-snapshot/);
assert.doesNotMatch(native, /fn persist_state|fn load_project_state|fn record_cpl_action/);
for (const operation of [
"SessionCreated", "SessionActivated", "SessionTitleRevised", "PersonaChanged",
"ChallengeChanged", "GenreChanged", "LoreChanged", "ProviderContextChanged",
"CloudApprovalChanged", "HumanTurnCreated", "AssistantTurnCreated", "IdeasChanged",
"DraftingPaperTurnAppended", "DraftingPaperRevised", "ProviderInvocationRequested",
"ProviderInvocationResponded", "ProviderInvocationFailed", "DistillationDisposed",
"ExternalContentDeclared",
]) assert.match(phase1, new RegExp(operation));
for (const recordType of [
"transcript-turn", "conversation-session", "idea-revision", "drafting-paper-revision",
"invocation-request", "invocation-response", "invocation-failure", "disposition-revision",
"source-declaration", "voice-transcription",
]) assert.match(phase1, new RegExp(recordType));
assert.match(phase1, /reconstruct_from_events/);
assert.match(phase1, /record_type == "phase1-operation"/);
assert.match(phase1, /operational_state: None/);
});
test("records provider intent before I/O and records every outcome afterward", async () => {
const native = await source("../src-tauri/src/lib.rs");
const generate = native.slice(native.indexOf("fn generate_text("), native.indexOf("#[cfg_attr(mobile", native.indexOf("fn generate_text(")));
const request = generate.indexOf("ProviderInvocationRequested");
const send = generate.indexOf("request.send()");
const response = generate.indexOf("ProviderInvocationResponded");
const failure = generate.indexOf("ProviderInvocationFailed");
assert.ok(request >= 0 && send > request, "request record must precede provider I/O");
assert.ok(response > send, "response record must follow provider I/O");
assert.ok(failure > send, "failure record must follow provider I/O");
assert.match(generate, /No CPL writer lock is held while provider I\/O runs/);
});
test("treats voice transcription as human text without retaining audio", async () => {
const [frontend, phase1] = await Promise.all([
source("../src/Thinkloom.tsx"),
source("../src-tauri/src/provenance/phase1.rs"),
]);
assert.match(frontend, /inputMode.*voice_transcription/s);
assert.match(phase1, /audio_retained": false/);
assert.match(phase1, /audio_reference": Value::Null/);
assert.match(phase1, /voice_transcription_retains_text_but_no_audio_reference_or_digest/);
});
+55
View File
@@ -0,0 +1,55 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
test("establishes the exact CPL 1.0 project marker and conforming layout", async () => {
const [format, native, writer] = await Promise.all([
readFile(new URL("../src-tauri/src/project_format.rs", import.meta.url), "utf8"),
readFile(new URL("../src-tauri/src/lib.rs", import.meta.url), "utf8"),
readFile(new URL("../src-tauri/src/provenance/writer.rs", import.meta.url), "utf8"),
]);
for (const exact of [
'PROJECT_FORMAT: &str = "thinkloom-cpl"',
'PROJECT_FORMAT_VERSION: &str = "1.0"',
'PROVENANCE_CONFORMANCE: &str = "cpl-1.0"',
]) assert.match(format, new RegExp(exact.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")));
for (const path of ["records", "provenance/ledger/active", "provenance/ledger/sealed", "reports", ".app/locks", ".app/temp", ".app/recovery"]) {
assert.match(format, new RegExp(path.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")));
}
assert.match(native, /project_format: project_format::PROJECT_FORMAT/);
assert.match(native, /project_format_version: project_format::PROJECT_FORMAT_VERSION/);
assert.match(native, /provenance_conformance: project_format::PROVENANCE_CONFORMANCE/);
assert.match(writer, /\.app\/locks/);
assert.match(writer, /\.app\/temp\/staging/);
assert.doesNotMatch(`${native}\n${writer}`, /\.thinkloom/);
});
test("classifies legacy projects before recovery and exposes preservation-only controls", async () => {
const [format, native, frontend] = await Promise.all([
readFile(new URL("../src-tauri/src/project_format.rs", import.meta.url), "utf8"),
readFile(new URL("../src-tauri/src/lib.rs", import.meta.url), "utf8"),
readFile(new URL("../src/Thinkloom.tsx", import.meta.url), "utf8"),
]);
assert.match(format, /LegacyPreviewReadOnly/);
assert.match(format, /schema_version_without_marker_is_legacy_and_inspection_is_read_only/);
assert.match(format, /preservation_archive_retains_source_bytes_without_changing_source/);
assert.match(format, /Not verified, converted, or CPL-conforming/);
assert.match(format, /LEGACY_ARCHIVE_INSIDE_PROJECT/);
const inspect = native.indexOf("project_format::inspect_project(&root)");
const recover = native.indexOf("CplService::new(&root, &manifest.project_id).recover()", inspect);
assert.ok(inspect >= 0 && recover > inspect, "project inspection must precede CPL recovery");
assert.match(native, /inspection\.classification != project_format::ProjectClassification::CplConforming/);
assert.match(native, /set_read_only_project/);
assert.match(native, /show_project_folder/);
assert.match(native, /create_legacy_preservation_archive/);
assert.match(native, /LEGACY_BACKUP_REFUSED/);
assert.match(frontend, /Legacy preview project/);
assert.match(frontend, /Show project folder/);
assert.match(frontend, /Create preservation archive/);
assert.match(frontend, /No migration was attempted/);
});
+151 -12
View File
@@ -24,43 +24,74 @@ function validator() {
}
const requiredSchemas = [
"assertion-evaluation", "backup-manifest", "chain-head", "content-reference", "conversation-session", "derived-index-manifest",
"disposition-revision", "edit-transaction", "encrypted-key-envelope", "idea", "idea-revision",
"assertion-evaluation", "backup-manifest", "chain-head", "composition-operation", "content-reference", "contribution-map",
"conversation-session", "deposit-snapshot", "derived-index-manifest", "disposition-revision", "edit-transaction",
"encrypted-key-envelope", "expression-segment", "harp-export-manifest", "human-authorship-record", "idea", "idea-revision",
"invocation-failure", "invocation-request", "invocation-response", "invocation-state", "invocation-stream-state",
"invocation-stream-summary", "ledger-segment-manifest", "manuscript-revision", "model-capability-snapshot",
"model-configuration-snapshot", "project-key-manifest", "project-manifest", "prompt-template",
"prompt-template-reference", "provenance-assertion", "provenance-event", "provenance-policy", "purge-manifest", "record-envelope",
"recovery-key-envelope", "release-manifest", "release-state", "sanitized-export-manifest", "text-fragment-reference",
"transcript-correction", "transcript-normalization", "transcript-turn", "verification-report", "write-intent",
"recovery-key-envelope", "registration-policy-profile", "release-manifest", "release-state", "sanitized-export-manifest",
"text-fragment-reference", "transcript-correction", "transcript-normalization", "transcript-turn", "verification-report", "write-intent",
];
test("catalogs every approved Stage 2 schema under Draft 2020-12", () => {
assert.equal(catalog.catalog_version, "1.0");
assert.equal(catalog.catalog_version, "1.1");
assert.equal(catalog.package_version, "0.5.2");
assert.equal(catalog.provenance_schema_version, "1.0");
assert.equal(catalog.application_version, "0.4.0");
assert.deepEqual(catalog.compatible_application_versions, ["0.4.0"]);
assert.equal(catalog.application_version, "0.5.2");
assert.deepEqual(catalog.compatible_application_versions, ["0.4.0", "0.5.0", "0.5.1", "0.5.2", "0.5.3", "0.5.4", "0.5.5", "0.5.6", "0.5.7", "0.5.8", "0.5.9", "0.5.10", "0.5.11", "0.6.0"]);
assert.equal(catalog.cpl_runtime_target, "0.6.0");
assert.equal(catalog.native_writer_conformance, false);
assert.match(catalog.assertion_semantics_compatibility, /v0\.4.+remain valid/i);
assert.equal(catalog.dialect, "https://json-schema.org/draft/2020-12/schema");
assert.deepEqual(catalog.schemas.map(({ name }) => name), requiredSchemas);
assert.equal(new Set(catalog.schemas.map(({ id }) => id)).size, requiredSchemas.length);
for (const entry of catalog.schemas) {
const isCompositionExtension = ["composition-operation", "expression-segment", "contribution-map", "deposit-snapshot", "registration-policy-profile", "human-authorship-record", "harp-export-manifest"].includes(entry.name);
assert.equal(entry.introduced_in_application_version, isCompositionExtension ? "0.5.2" : "0.4.0");
assert.ok(entry.compatible_application_versions.includes("0.5.3"));
assert.ok(entry.compatible_application_versions.includes("0.5.10"));
assert.ok(entry.compatible_application_versions.includes("0.5.11"));
assert.ok(entry.compatible_application_versions.includes("0.6.0"));
if (!isCompositionExtension) assert.ok(entry.compatible_application_versions.includes("0.4.0"));
}
for (const { schema } of schemaEntries) {
assert.equal(schema.additionalProperties, false, `${schema.$id} must be closed at its top level`);
}
});
test("publishes complete versioned assertion registries", async () => {
const expected = ["assertion-boundary-kinds", "assertion-confidence-dimensions", "assertion-evaluation-statuses", "assertion-evidence-classes", "assertion-lifecycle-phases", "assertion-reason-codes"];
assert.deepEqual(catalog.registries.map(({ name }) => name).sort(), expected);
test("publishes complete versioned assertion and composition registries", async () => {
const legacyExpected = ["assertion-boundary-kinds", "assertion-confidence-dimensions", "assertion-evaluation-statuses", "assertion-evidence-classes", "assertion-lifecycle-phases", "assertion-reason-codes"];
const compositionExpected = ["composition-assertion-predicates", "composition-operation-kinds", "contribution-map-layers", "harp-explanation-codes", "harp-limitation-codes", "recorded-origin-kinds", "registration-treatment-suggestions", "transformation-relationships"];
assert.deepEqual(catalog.registries.map(({ name }) => name).sort(), [...legacyExpected, ...compositionExpected].sort());
for (const entry of catalog.registries) {
const registry = await readJson(entry.file);
assert.equal(registry.registry_version, "1.0");
const isCompositionExtension = compositionExpected.includes(entry.name);
assert.equal(registry.registry_version, isCompositionExtension ? "1.1" : "1.0");
assert.equal(registry.provenance_schema_version, "1.0");
assert.equal(registry.application_version, "0.4.0");
assert.equal(registry.application_version, isCompositionExtension ? "0.5.2" : "0.4.0");
assert.equal(registry.introduced_in_application_version, isCompositionExtension ? "0.5.2" : "0.4.0");
assert.ok(registry.compatible_application_versions.includes("0.5.3"));
assert.ok(registry.compatible_application_versions.includes("0.5.10"));
assert.ok(registry.compatible_application_versions.includes("0.5.11"));
assert.ok(registry.compatible_application_versions.includes("0.6.0"));
assert.ok(registry.entries.length > 0);
assert.ok(registry.entries.every(({ meaning }) => typeof meaning === "string" && meaning.length > 0));
assert.equal(new Set(registry.entries.map(({ code }) => code)).size, registry.entries.length);
}
});
test("keeps composition dimensions independent and preserves v0.4 assertion semantics", async () => {
const registries = Object.fromEntries(await Promise.all(catalog.registries.map(async (entry) => [entry.name, await readJson(entry.file)])));
assert.deepEqual(registries["composition-operation-kinds"].entries.map(({ code }) => code), ["insert", "delete", "replace", "move", "paste", "transcription", "ai_acceptance", "restoration"]);
assert.deepEqual(registries["recorded-origin-kinds"].entries.map(({ code }) => code), ["recorded_direct_human_input", "human_expressive_input_via_transcription", "accepted_ai_output", "imported_or_pasted", "system_restoration", "unattested"]);
assert.deepEqual(registries["composition-assertion-predicates"].entries.map(({ code }) => code), ["derived_from", "generated_by", "modified_by_human", "selected_by_human", "arranged_by_human", "included_in_deposit"]);
assert.ok(registries["contribution-map-layers"].entries.some(({ code }) => code === "selection_arrangement"));
assert.ok(registries["registration-treatment-suggestions"].entries.some(({ code }) => code === "manual_review_required"));
assert.deepEqual(registries["assertion-confidence-dimensions"].entries.map(({ code }) => code), ["integrity", "identity", "chronology", "derivation", "authorship", "completeness"]);
assert.equal(registries["assertion-confidence-dimensions"].application_version, "0.4.0");
});
test("accepts every valid fixture", async () => {
const ajv = validator();
for (const entry of schemaEntries) {
@@ -200,6 +231,11 @@ test("reproduces every defined self-digest identity", async () => {
assert.equal(sha256(vector.protected_record.identity), vector.protected_record.digest);
assert.equal(releaseMerkleRoot(vector.release_manifest.merkle_identity), vector.release_manifest.digest);
assert.ok(vector.release_manifest.excluded_paths.includes("release-manifest.json"));
for (const [key, schemaName] of [["contribution_map", "contribution-map"], ["registration_policy_profile", "registration-policy-profile"], ["human_authorship_record", "human-authorship-record"], ["harp_export_manifest", "harp-export-manifest"]]) {
assert.equal(sha256(vector[key].identity), vector[key].digest, key);
const validate = ajv.getSchema(`https://thinkloom.app/schemas/provenance/1.0/${schemaName}.schema.json`);
assert.equal(validate(vector[key].complete_record), true, `${key}: ${ajv.errorsText(validate.errors)}`);
}
});
test("validates key recovery materials and sanitized non-mutating export disclosure", async () => {
@@ -215,6 +251,20 @@ test("validates key recovery materials and sanitized non-mutating export disclos
const validateSanitized = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/sanitized-export-manifest.schema.json");
assert.equal(validateSanitized(sanitizedVector.manifest), true, ajv.errorsText(validateSanitized.errors));
assert.equal(sha256(sanitizedVector.manifest.omission_rules), sanitizedVector.manifest.rules_sha256);
assert.equal(sanitizedVector.manifest.completeness_claim, "selective_disclosed_subset");
assert.deepEqual(sanitizedVector.manifest.omission_rules.map((rule) => rule.category), [
"private_conversation",
"rejected_model_output",
"credential_authorization_material",
"personal_identifier",
"internal_path",
"provider_metadata_not_required",
"protected_source_body",
]);
for (const rule of sanitizedVector.manifest.omission_rules) {
const { disclosure_sha256, ...identity } = rule;
assert.equal(sha256(identity), disclosure_sha256);
}
assert.equal(sanitizedVector.source_chain_head_before, sanitizedVector.source_chain_head_after);
assert.ok(sanitizedVector.exported_record_count < sanitizedVector.source_record_count);
});
@@ -319,3 +369,92 @@ test("keeps assertion status, reason, confidence, and evidence semantics registr
assert.deepEqual(evidenceRegistry.entries.map(({ code }) => code), ["mandatory_live", "mandatory_retained", "advisory", "shadow"]);
assert.deepEqual(evidenceRegistry.entries.map(({ exact_effect }) => exact_effect), ["required", "required", "may_degrade", "no_authority"]);
});
test("enforces composition-operation origin rules and refuses unknown exact classifications", async () => {
const ajv = validator();
const vector = await readJson("vectors", "composition-and-harp-classification.json");
const validateOperation = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/composition-operation.schema.json");
const validateSegment = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/expression-segment.schema.json");
const validateHarp = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/human-authorship-record.schema.json");
for (const [name, operation] of Object.entries(vector.operations)) {
assert.equal(validateOperation(operation), true, `${name}: ${ajv.errorsText(validateOperation.errors)}`);
}
assert.equal(vector.operations.paste.recorded_origin_kind, "imported_or_pasted");
assert.notEqual(vector.operations.paste.recorded_origin_kind, "recorded_direct_human_input");
assert.equal(vector.operations.ai_acceptance.recorded_origin_kind, "accepted_ai_output");
assert.ok(vector.operations.ai_acceptance.invocation_id);
assert.ok(vector.operations.ai_acceptance.disposition_id);
assert.equal(validateSegment(vector.exact_expression_segment), true, ajv.errorsText(validateSegment.errors));
assert.equal(validateHarp(vector.exact_harp), true, ajv.errorsText(validateHarp.errors));
for (const [name, instance] of Object.entries(vector.forbidden_exact_segments)) {
assert.equal(validateSegment(instance), false, `segment ${name} must not validate as exact`);
}
for (const [name, instance] of Object.entries(vector.forbidden_exact_harps)) {
assert.equal(validateHarp(instance), false, `HARP ${name} must not validate as exact`);
}
assert.deepEqual(vector.independent_dimensions, ["recorded_origin", "transformation", "selection_arrangement", "evidentiary_evaluation", "suggested_registration_treatment"]);
for (const field of vector.prohibited_claim_fields) assert.equal(Object.hasOwn(vector.exact_harp, field), false, field);
});
test("reproduces complete ordered contribution maps independently of input order", async () => {
const ajv = validator();
const vector = await readJson("vectors", "contribution-map-determinism.json");
const validate = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/contribution-map.schema.json");
assert.equal(validate(vector.deterministic_map), true, ajv.errorsText(validate.errors));
const sortSegments = (segments) => [...segments].sort((left, right) => left.segment_sequence - right.segment_sequence || (left.segment_id < right.segment_id ? -1 : left.segment_id > right.segment_id ? 1 : 0));
const canonicalOrders = vector.input_orders.map((segments) => canonicalize(sortSegments(segments)));
assert.equal(canonicalOrders[0], canonicalOrders[1]);
assert.equal(sha256(canonicalize(vector.deterministic_map)), vector.canonical_map_sha256);
const { deterministic_map: map } = vector;
const mapIdentity = { ...map };
delete mapIdentity.contribution_map_sha256;
assert.equal(sha256(mapIdentity), map.contribution_map_sha256);
assert.equal(map.coverage.coverage_status, "complete");
assert.equal(map.coverage.recorded_positions, map.coverage.denominator);
let cursor = 0;
for (const segment of map.segments) {
assert.equal(segment.range.coordinate_system, "unicode_scalar");
assert.equal(segment.range.start, cursor, `${segment.segment_id} must begin at the prior end`);
assert.ok(segment.range.end > segment.range.start);
assert.equal(segment.range.end - segment.range.start, segment.normalized_unicode_scalar_length);
cursor = segment.range.end;
}
assert.equal(cursor, map.coverage.denominator);
assert.ok(map.layers.includes("selection_arrangement"));
assert.ok(map.layers.includes("recorded_origin"));
});
test("binds HARP to one exact deposit and makes later revisions stale without rewriting history", async () => {
const ajv = validator();
const vector = await readJson("vectors", "harp-deposit-staleness.json");
const validateDeposit = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/deposit-snapshot.schema.json");
const validateHarp = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/human-authorship-record.schema.json");
const validateManifest = ajv.getSchema("https://thinkloom.app/schemas/provenance/1.0/harp-export-manifest.schema.json");
assert.equal(validateDeposit(vector.deposit_snapshot), true, ajv.errorsText(validateDeposit.errors));
assert.equal(validateHarp(vector.current_harp), true, ajv.errorsText(validateHarp.errors));
assert.equal(validateHarp(vector.stale_after_edit), true, ajv.errorsText(validateHarp.errors));
assert.equal(validateManifest(vector.export_manifest), true, ajv.errorsText(validateManifest.errors));
assert.equal(vector.current_harp.deposit.deposit_sha256, vector.deposit_snapshot.deposit_sha256);
assert.equal(vector.current_harp.deposit.manuscript_revision_id, vector.deposit_snapshot.manuscript_revision_id);
assert.equal(vector.current_harp.cpl_binding.chain_head, vector.deposit_snapshot.cpl_chain_head);
assert.equal(vector.current_harp.applicability_status, "current");
assert.equal(vector.stale_after_edit.applicability_status, "stale");
assert.notEqual(vector.stale_after_edit.deposit.manuscript_revision_sha256, vector.current_harp.deposit.manuscript_revision_sha256);
assert.equal(vector.current_harp.suggested_registration_language.user_approved, true);
for (const [record, digestField] of [[vector.current_harp, "harp_sha256"], [vector.stale_after_edit, "harp_sha256"], [vector.export_manifest, "manifest_sha256"]]) {
const identity = { ...record };
delete identity[digestField];
assert.equal(sha256(identity), record[digestField]);
}
const fileByRole = Object.fromEntries(vector.export_manifest.files.map((file) => [file.role, file]));
assert.equal(fileByRole.machine_readable_harp.sha256, vector.current_harp.harp_sha256);
assert.equal(fileByRole.deposit_copy.sha256, vector.deposit_snapshot.deposit_sha256);
assert.equal(vector.export_manifest.harp_sha256, vector.current_harp.harp_sha256);
assert.equal(vector.export_manifest.deposit_sha256, vector.deposit_snapshot.deposit_sha256);
assert.doesNotMatch(JSON.stringify({ claim_summary: vector.current_harp.claim_summary, coverage: vector.current_harp.coverage, language: vector.current_harp.suggested_registration_language }), /human\s*(?:percentage|%)|ai\s*(?:percentage|%)|copyright verified|originality proven|authorship certified/i);
});
+75
View File
@@ -0,0 +1,75 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("connects the milestone 9 CPL explorer and HARP preparation views", async () => {
const [app, workspace, native, modules] = await Promise.all([
source("../src/Thinkloom.tsx"),
source("../src/ProvenanceWorkspace.tsx"),
source("../src-tauri/src/lib.rs"),
source("../src-tauri/src/provenance/mod.rs"),
]);
assert.match(app, /import ProvenanceWorkspace/);
assert.match(app, /<ProvenanceWorkspace onNotice=/);
assert.match(modules, /pub mod explorer;/);
assert.match(native, /fn load_cpl_explorer/);
assert.match(native, /fn prepare_harp/);
assert.match(workspace, /CPL explorer/);
assert.match(workspace, /Prepare HARP/);
assert.match(workspace, /load_cpl_explorer/);
assert.match(workspace, /prepare_harp/);
});
test("renders native evidence, lineage, evaluation, and verification boundaries", async () => {
const workspace = await source("../src/ProvenanceWorkspace.tsx");
for (const requirement of [
"Native CPL verification",
"Composition timeline",
"Expression lineage",
"Assertions and current evaluations",
"Underlying records",
"Exact, degraded, stale, and unverified",
"Statement → assertion → evaluation → record",
]) assert.match(workspace, new RegExp(requirement));
assert.doesNotMatch(workspace, /human_percentage/);
assert.match(workspace, /not a human-authorship score/);
});
test("keeps evidence categories and legal limits explicit", async () => {
const workspace = await source("../src/ProvenanceWorkspace.tsx");
for (const category of [
"Evidence fact",
"User declaration",
"Derived classification",
"Suggested application language",
"Legal determination not made",
]) assert.match(workspace, new RegExp(category));
assert.match(workspace, /does not determine legal authorship, originality, copyrightability, ownership, or registrability/);
});
test("requires explicit review and approval before native HARP generation", async () => {
const workspace = await source("../src/ProvenanceWorkspace.tsx");
for (const action of [
"Freeze or select the exact deposit",
"Confirm the author identity declaration",
"Review AI systems used",
"Review final-text classifications",
"Resolve or accept evidence boundaries",
"Preview suggested registration language",
"Choose the archive and explicitly approve",
"I explicitly approve HARP generation",
"Approve and generate HARP",
]) assert.match(workspace, new RegExp(action));
assert.match(workspace, /userApproved: true/);
assert.match(workspace, /generate_harp/);
assert.match(workspace, /disabled=\{!approved \|\| busy\}/);
});
+89
View File
@@ -0,0 +1,89 @@
import assert from "node:assert/strict";
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const source = (relative) => readFile(path.join(root, relative), "utf8");
test("maps every Milestone 11 requirement to executable verification", async () => {
const files = [
"tests/provenance-schema.test.mjs",
"tests/native-cpl.test.mjs",
"tests/phase1-provenance.test.mjs",
"tests/composition-provenance.test.mjs",
"tests/contribution-map.test.mjs",
"tests/harp-generation.test.mjs",
"tests/provenance-ui.test.mjs",
"tests/harp-export.test.mjs",
"src-tauri/src/provenance/tests.rs",
"src-tauri/src/provenance/composition.rs",
"src-tauri/src/provenance/contribution_map.rs",
"src-tauri/src/provenance/harp.rs",
"src-tauri/src/provenance/export.rs",
"src-tauri/src/provenance/phase1.rs",
"src-tauri/src/provenance/verifier.rs",
"src/Thinkloom.tsx",
];
const corpus = (await Promise.all(files.map(source))).join("\n");
const requirements = new Map([
["schema fixtures and deterministic regeneration", /(?=[\s\S]*accepts every valid fixture)(?=[\s\S]*rejects every invalid fixture)(?=[\s\S]*deterministic derived-index)/i],
["canonical JSON, Unicode, timestamp, path, and JSONL vectors", /(?=[\s\S]*canonical JSON)(?=[\s\S]*canonical timestamps)(?=[\s\S]*repository-relative paths)(?=[\s\S]*JSONL)/i],
["duplicate actions and concurrent writers", /(?=[\s\S]*retries_are_idempotent_and_conflicts_are_rejected)(?=[\s\S]*os_writer_lock_serializes_concurrent_actions)/],
["failure after every durable write phase", /(?=[\s\S]*every_durable_boundary_recovers_and_retries_safely)(?=[\s\S]*every_segment_rotation_boundary_recovers_without_sequence_loss)/],
["segment rotation and cross-segment verification", /rotates_and_verifies_cross_segment_linkage/],
["manual typing and deletion", /(?=[\s\S]*typed_records_reconstruct_phase1)(?=[\s\S]*transform_delete)/],
["paste and import", /(?=[\s\S]*ImportedOrPasted)(?=[\s\S]*onPasteCapture)/],
["human revision of AI-origin material", /transform_revise/],
["AI transformation of human material", /transform_ai/],
["partial AI acceptance", /unicode_scalar_diff_and_partial_ai_ranges_are_exact/],
["selection and arrangement without origin changes", /selection_and_arrangement_preserve_each_source_origin/],
["voice transcription without audio persistence", /voice_transcription_retains_text_but_no_audio_reference_or_digest/],
["restore and checkpoint lineage", /(?=[\s\S]*replays_manual_paste_ai_revision_and_restoration_with_lineage)(?=[\s\S]*create_checkpoint)/],
["unknown and unattested spans", /reports_unattested_coverage_without_calling_it_non_human/],
["deposit and HARP staleness", /(?=[\s\S]*binds HARP to one exact deposit)(?=[\s\S]*frozen_map_becomes_stale)/],
["complete segment coverage across complex Unicode", /complex_unicode_has_complete_contiguous_segment_coverage/],
["native verifier and frontend consistency", /(?=[\s\S]*require_release_verification)(?=[\s\S]*verify_provenance)(?=[\s\S]*VERIFIED_WITH_WARNINGS)/],
["sanitized archive disclosure", /discloses and hash-binds every required sanitized omission category/],
["release blocking", /release_gate_accepts_only_complete_safe_native_verification/],
]);
for (const [requirement, pattern] of requirements) {
assert.match(corpus, pattern, `Missing executable coverage: ${requirement}`);
}
});
test("prohibits scores and affirmative legal conclusions in shipped source", async () => {
const roots = ["src", "src-tauri/src"];
const productionFiles = [];
for (const directory of roots) {
const entries = await readdir(path.join(root, directory), { recursive: true, withFileTypes: true });
for (const entry of entries) {
if (!entry.isFile() || !/\.(?:rs|tsx|ts|css)$/.test(entry.name)) continue;
productionFiles.push(path.join(entry.parentPath, entry.name));
}
}
const corpus = (await Promise.all(productionFiles.map((file) => readFile(file, "utf8")))).join("\n");
for (const prohibited of [
/\b\d+(?:\.\d+)?%\s+human\b/i,
/human[- ]authorship\s+(?:score|percentage)\s*[:=]\s*\d/i,
/Thinkloom\s+(?:determines|certifies|proves)\s+(?:legal authorship|originality|copyrightability|ownership|registrability)/i,
/(?:legal authorship|copyrightability)\s*[:=]\s*["']?(?:yes|verified|valid)/i,
]) assert.doesNotMatch(corpus, prohibited);
});
test("uses the same native verification statuses in the frontend release gate", async () => {
const [records, frontend, verifier, native] = await Promise.all([
source("src-tauri/src/provenance/records.rs"),
source("src/Thinkloom.tsx"),
source("src-tauri/src/provenance/verifier.rs"),
source("src-tauri/src/lib.rs"),
]);
for (const status of ["VERIFIED", "VERIFIED_WITH_WARNINGS", "INCOMPLETE", "FAILED", "UNSAFE"]) {
assert.match(frontend, new RegExp(`\\b${status}\\b`));
}
assert.match(records, /VerifiedWithWarnings[\s\S]*Incomplete[\s\S]*Failed[\s\S]*Unsafe/);
assert.match(verifier, /Verified \| VerificationStatus::VerifiedWithWarnings => Ok/);
assert.match(native, /verify_project\(&root, &manifest\.project_id\)[\s\S]*require_release_verification/);
assert.doesNotMatch(frontend, /status\s*===\s*["']VALID["']/);
});
+47
View File
@@ -0,0 +1,47 @@
import assert from "node:assert/strict";
import { access, readFile, readdir, stat } from "node:fs/promises";
import path from "node:path";
import { spawn } from "node:child_process";
import test from "node:test";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const releaseRoot = path.join(root, "src-tauri", "target", "release");
const packageJson = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
const tauriConfig = JSON.parse(await readFile(path.join(root, "src-tauri", "tauri.conf.json"), "utf8"));
test("packaged Windows release launches and includes both installer formats", { skip: process.platform !== "win32", timeout: 20_000 }, async () => {
assert.equal(tauriConfig.version, packageJson.version);
assert.deepEqual(new Set(tauriConfig.bundle.targets), new Set(["msi", "nsis"]));
const executable = path.join(releaseRoot, "thinkloom.exe");
await access(executable);
const executableBytes = await readFile(executable);
assert.deepEqual(executableBytes.subarray(0, 2), Buffer.from("MZ"));
assert.ok((await stat(executable)).size > 1_000_000, "release executable is unexpectedly small");
const bundleRoot = path.join(releaseRoot, "bundle");
const entries = await readdir(bundleRoot, { recursive: true, withFileTypes: true });
const artifacts = entries
.filter((entry) => entry.isFile())
.map((entry) => path.join(entry.parentPath, entry.name));
const msi = artifacts.find((file) => file.toLowerCase().endsWith(".msi") && file.includes(packageJson.version));
const nsis = artifacts.find((file) => file.toLowerCase().endsWith("setup.exe") && file.includes(packageJson.version));
assert.ok(msi, `no ${packageJson.version} MSI bundle found`);
assert.ok(nsis, `no ${packageJson.version} NSIS bundle found`);
assert.deepEqual((await readFile(msi)).subarray(0, 8), Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]));
assert.deepEqual((await readFile(nsis)).subarray(0, 2), Buffer.from("MZ"));
const child = spawn(executable, [], { cwd: releaseRoot, windowsHide: true, stdio: "ignore" });
await new Promise((resolve, reject) => {
child.once("error", reject);
child.once("spawn", resolve);
});
await new Promise((resolve) => setTimeout(resolve, 2_500));
if (child.exitCode === null) {
assert.equal(child.kill(), true, "packaged application could not be stopped after launch smoke test");
await new Promise((resolve) => child.once("exit", resolve));
} else {
assert.equal(child.exitCode, 0, "packaged application exited unsuccessfully during launch smoke test");
}
});