From 3b5701acfdf19c7fca7b1476806baf5ee68044c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valent=C3=ADn=20Quiroz=20de=20la=20Sierra?= <85706426+vqsierra@users.noreply.github.com> Date: Fri, 13 Nov 2026 12:36:12 -0500 Subject: [PATCH] Key file: say "a secure place" instead of Teams, and use the local time zone The key file told every researcher to keep a copy in their Teams channel files and wrote its dates in New York time. Both came from the first study. The file now names a secure place the study has approved, and writes dates in the time zone of the computer that saves it. session-client 0.1.4. The JSON data section and the format are unchanged, so files saved by 0.1.3 open as before. --- docs/specs/SPEC-session-client.md | 8 ++++---- package-lock.json | 2 +- packages/session-client/package.json | 2 +- packages/session-client/src/file.ts | 10 +++++----- packages/session-client/test/file.test.ts | 1 + 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/specs/SPEC-session-client.md b/docs/specs/SPEC-session-client.md index 69c6de8..803f990 100644 --- a/docs/specs/SPEC-session-client.md +++ b/docs/specs/SPEC-session-client.md @@ -126,15 +126,15 @@ Reserved for mVote, as additive methods only: `ParticipantSession.publishShared( ## Session key file -The file is Markdown so it can be read in Obsidian or Teams. The JSON block after the marker comment is the only source of truth; the human-readable parts are regenerated from it on every save. `parseSessionFile` rejects any file whose JSON is missing, invalid, of an unknown `format`, or has malformed fields. +The file is Markdown so it can be read in any text editor or Markdown viewer. The JSON block after the marker comment is the only source of truth; the human-readable parts are regenerated from it on every save. `parseSessionFile` rejects any file whose JSON is missing, invalid, of an unknown `format`, or has malformed fields. ````markdown # MalverdeSoftware session key: Youth CAB text message sort > **Keep this file private.** Anyone who has it can read every response in this session. -> Keep it only on your computer and in your study team's Teams channel files. Do not put it -> in a git repository, email it, or paste it into a chat or AI tool. If it is lost, the -> responses cannot be recovered by anyone. +> Keep it only on your computer and in a secure place your study has approved for its files. +> Do not put it in a git repository, email it, or paste it into a chat or AI tool. If it is +> lost, the responses cannot be recovered by anyone. | | | |---|---| diff --git a/package-lock.json b/package-lock.json index 3e9b0f8..3bd3e98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2909,7 +2909,7 @@ }, "packages/session-client": { "name": "@malverdesoftware/session-client", - "version": "0.1.3", + "version": "0.1.4", "license": "Apache-2.0", "dependencies": { "@malverdesoftware/sealed-envelope": "0.1.2" diff --git a/packages/session-client/package.json b/packages/session-client/package.json index 6b1cf33..3e4e47c 100644 --- a/packages/session-client/package.json +++ b/packages/session-client/package.json @@ -1,6 +1,6 @@ { "name": "@malverdesoftware/session-client", - "version": "0.1.3", + "version": "0.1.4", "license": "Apache-2.0", "description": "Session lifecycle for MalverdeSoftware apps: session key files, personal links with passcodes, sealed drafts and submissions.", "type": "module", diff --git a/packages/session-client/src/file.ts b/packages/session-client/src/file.ts index 78aa7ab..afc3b83 100644 --- a/packages/session-client/src/file.ts +++ b/packages/session-client/src/file.ts @@ -146,8 +146,8 @@ export async function readDefinition(file: SessionFile): Promise { return (payload as { appDefinition: unknown }).appDefinition; } -const longDate = (iso: string) => - new Intl.DateTimeFormat("en-US", { dateStyle: "long", timeZone: "America/New_York" }).format(new Date(iso)); +// In the time zone of the computer that saves the file, which is where the researcher reads it. +const longDate = (iso: string) => new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(new Date(iso)); export function renderSessionFile(file: SessionFile): string { const active = file.participants.filter((p) => !p.revokedAt); @@ -156,9 +156,9 @@ export function renderSessionFile(file: SessionFile): string { `# MalverdeSoftware session key: ${file.title}`, "", "> **Keep this file private.** Anyone who has it can read every response in this session.", - "> Keep it only on your computer and in your study team's Teams channel files. Do not put it", - "> in a git repository, email it, or paste it into a chat or AI tool. If it is lost, the", - "> responses cannot be recovered by anyone.", + "> Keep it only on your computer and in a secure place your study has approved for its files.", + "> Do not put it in a git repository, email it, or paste it into a chat or AI tool. If it is", + "> lost, the responses cannot be recovered by anyone.", "", "| | |", "|---|---|", diff --git a/packages/session-client/test/file.test.ts b/packages/session-client/test/file.test.ts index d2d67d8..30cf50d 100644 --- a/packages/session-client/test/file.test.ts +++ b/packages/session-client/test/file.test.ts @@ -43,6 +43,7 @@ test("the session file renders to Markdown and parses back losslessly", async () assert.deepEqual(parseSessionFile(markdown), file); assert.match(markdown, /Keep this file private/); assert.match(markdown, /cannot be recovered by anyone/); + assert.match(markdown, /a secure place your study has approved/); assert.ok(markdown.includes(`| Y01 | ${file.participants[0]!.link} | ${file.participants[0]!.passcode} |`)); assert.ok(!markdown.includes(`| Y02 |`), "revoked participants are left out of the link table"); assert.ok(markdown.includes(`| Y03 | ${file.participants[2]!.link} | ${file.participants[2]!.passcode} |`));