summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--backend-handoff.md32
-rw-r--r--notes/assumptions-log.md2
-rw-r--r--scripts/network-policy.test.ts37
-rw-r--r--scripts/probe-tab.ts (renamed from scripts/probe-tab-PII_REDACTED_CONVERSATION_SHORT_01.ts)16
-rw-r--r--scripts/vite-network-guard.test.ts18
-rw-r--r--src/app/resolve-http-url.test.ts8
-rw-r--r--src/app/resolve-ws-url.test.ts8
-rw-r--r--src/features/chat/ui/ChatView.test.ts4
8 files changed, 89 insertions, 36 deletions
diff --git a/backend-handoff.md b/backend-handoff.md
index d1d0756..98cee83 100644
--- a/backend-handoff.md
+++ b/backend-handoff.md
@@ -18,6 +18,38 @@
> ?archived=`) was made in the same branch and the FE consumes it via the `file:` deps (dists
> rebuilt). Recorded below.
+## Milestone: Current frontend privacy cleanup (`feature/privacy-cleanup`, 2026-10-24)
+
+**Slice status:** COMPLETE; frontend-only cleanup verified.
+
+- Personal machine, tailnet DNS, IPv4 and IPv6 literals in network-policy and URL
+ resolver fixtures are replaced with synthetic names and documentation-only addresses
+ (`dev-node.tailnet.example`, `192.0.2.10`, `2001:db8::10`). The Vite guard fixture
+ uses the same reserved examples. Host/Origin, mapped-address, interface-selection,
+ fail-closed and env-override coverage is retained; runtime network policy is unchanged.
+- Ownership-repair documentation resolves the current user's name/group instead of
+ naming a personal local account. Generic home-directory fixtures, intentional public
+ repository-owner references, human identity and legitimate product/model references remain.
+- The conversation-specific diagnostic is now `scripts/probe-tab.ts <conversationId>`;
+ it requires an explicit target before any request and reports the actual chunk count.
+ Regression comments describe the duplicate-emission pattern without a real conversation id.
+- Audited tracked current files, including hidden config, contract mirrors and the lockfile.
+ No application defaults, stored user data, endpoint contracts or dependency pins changed.
+ Existing `file:../backend/packages/*` dependencies and additive mirrors remain unchanged
+- History/backups and removal of AI-attribution text from historical commit messages
+ belong to the coordinating task; this slice does not rewrite refs, merge or push.
+ Legitimate AI functionality/product references are not attribution and are retained.
+
+**Verification:** `bun install` succeeds without lockfile changes; `bun run typecheck`
+0 errors/0 warnings; `bun run test` 2,587 passed/2 opt-in skipped twice;
+`bun run check` clean (309 files); `bun run build` succeeds with existing Svelte
+capture and chunk-size advisories. Diagnostic usage fails safely without a target;
+an ephemeral loopback HTTP fixture verifies its two requests and dynamic chunk count.
+Final current-file scan has no known private literals. Report: `reports/privacy-cleanup.md`
+(gitignored).
+No browser-facing runtime or wire changes; no shared backend process started or modified.
+
## Milestone: Close-tab cascade subagents (`feature/close-tab-cascade-subagents`, 2026-10-24)
**Slice status: COMPLETE across the isolated frontend + backend feature branches.**
diff --git a/notes/assumptions-log.md b/notes/assumptions-log.md
index 89b057a..3291e28 100644
--- a/notes/assumptions-log.md
+++ b/notes/assumptions-log.md
@@ -45,7 +45,7 @@
2. **The `dist/` directory is owned by root** (likely from a previous root-run build). `bun run build`
fails with `EACCES` when Vite tries to empty `dist/assets`. This is an environment issue, not a
code issue — I verified the build succeeds with `--outDir dist-tmp` (a fresh directory I own, then
- removed). The user may want to `sudo chown -R PII_REDACTED_USER_01:PII_REDACTED_USER_01 dist/` or remove it.
+ removed). The user may want to `sudo chown -R "$(id -un):$(id -gn)" dist/` or remove it.
3. **Parser now rejects broadcasts missing `workspaceId`.** The WS parser (`logic.ts`) returns `null`
for `conversation.open` / `conversation.statusChanged` messages that lack a string `workspaceId`.
diff --git a/scripts/network-policy.test.ts b/scripts/network-policy.test.ts
index af7d870..4c3ed12 100644
--- a/scripts/network-policy.test.ts
+++ b/scripts/network-policy.test.ts
@@ -9,9 +9,10 @@ import {
tailscaleSelf,
} from "./network-policy";
+// Synthetic status: reserved .example DNS and documentation-only IPv4/IPv6 addresses.
const tailscaleStatusJson = `{"Self":{"HostName":"laptop",
- "DNSName":"PII_REDACTED_TAILSCALE_HOST_01.",
- "TailscaleIPs":["PII_REDACTED_DEV_IPV4_01","PII_REDACTED_DEV_IPV6_01"]}}`;
+ "DNSName":"dev-node.tailnet.example.",
+ "TailscaleIPs":["192.0.2.10","2001:db8::10"]}}`;
const allowedHosts = buildAllowedHosts({ tailscaleStatusJson, env: {} });
const allowedAddresses = new Set(["127.0.0.1", "::1", ...tailscaleSelf(tailscaleStatusJson).ips]);
const policy = { allowedHosts, allowedAddresses };
@@ -23,10 +24,10 @@ describe("backend-compatible hosts", () => {
["LOCALHOST.:34204", "localhost"],
["127.0.0.1:34204", "127.0.0.1"],
["[0:0:0:0:0:0:0:1]:34204", "::1"],
- ["PII_REDACTED_DEVICE_01", "PII_REDACTED_DEVICE_01"],
- ["PII_REDACTED_TAILSCALE_HOST_01.:34204", "PII_REDACTED_TAILSCALE_HOST_01"],
- ["PII_REDACTED_DEV_IPV4_01:34204", "PII_REDACTED_DEV_IPV4_01"],
- ["[PII_REDACTED_DEV_IPV6_01]:34204", "PII_REDACTED_DEV_IPV6_01"],
+ ["DEV-NODE", "dev-node"],
+ ["DEV-NODE.tailnet.example.:34204", "dev-node.tailnet.example"],
+ ["192.0.2.10:34204", "192.0.2.10"],
+ ["[2001:db8::10]:34204", "2001:db8::10"],
])("normalizes and allows %s", (raw, expected) => {
expect(normalizeHost(raw)).toBe(expected);
expect(checkHost(raw, allowedHosts)).toBe("ok");
@@ -34,7 +35,7 @@ describe("backend-compatible hosts", () => {
it.each(
(
- "evil.com|PII_REDACTED_DEVICE_01.evil.com|PII_REDACTED_DEV_IPV4_01.nip.io|laptop|localhost.evil.com|" +
+ "evil.com|dev-node.evil.com|192.0.2.10.nip.io|laptop|localhost.evil.com|" +
"sub.localhost|127.0.0.2|100.1.2.3|172.17.0.1|[email protected]|localhost/path|" +
"localhost:bad|localhost:65536|[::1]suffix|localhost\n|localhost..|%6cocalhost|"
).split("|"),
@@ -48,10 +49,10 @@ describe("backend-compatible hosts", () => {
...(json !== undefined ? { tailscaleStatusJson: json } : {}),
env: {
DISPATCH_ALLOWED_HOSTS: " EXTRA.example.:8080, https://evil.com",
- DISPATCH_TAILSCALE_NAME: "PII_REDACTED_DEVICE_01",
+ DISPATCH_TAILSCALE_NAME: "DEV-NODE",
},
};
- const expected = "localhost,127.0.0.1,::1,extra.example,PII_REDACTED_DEVICE_01".split(",");
+ const expected = "localhost,127.0.0.1,::1,extra.example,dev-node".split(",");
expect([...buildAllowedHosts(input)]).toEqual(expected);
expect(tailscaleSelf(json)).toEqual({ ips: [] });
});
@@ -61,20 +62,20 @@ describe("backend-compatible hosts", () => {
tailscaleStatusJson,
env: { DISPATCH_ALLOWED_HOSTS: "extra.example" },
});
- expect(hosts.has("PII_REDACTED_TAILSCALE_HOST_01")).toBe(true);
+ expect(hosts.has("dev-node.tailnet.example")).toBe(true);
expect(hosts.has("extra.example")).toBe(true);
expect(hosts.has("laptop")).toBe(false);
expect(tailscaleSelf(tailscaleStatusJson).hostName).toBe("laptop");
- expect(
- tailscaleSelf('{"Self":{"TailscaleIPs":[null,"evil.com","PII_REDACTED_DEV_IPV4_01"]}}').ips,
- ).toEqual(["PII_REDACTED_DEV_IPV4_01"]);
+ expect(tailscaleSelf('{"Self":{"TailscaleIPs":[null,"evil.com","192.0.2.10"]}}').ips).toEqual([
+ "192.0.2.10",
+ ]);
});
it.each([
["LOCALHOST.:34204", "localhost:34204"],
["[0:0:0:0:0:0:0:1]:34204", "[::1]:34204"],
["[::1]", "[::1]"],
- ["PII_REDACTED_DEVICE_01.", "PII_REDACTED_DEVICE_01"],
+ ["DEV-NODE.", "dev-node"],
])("preserves valid authorities for Vite: %s", (raw, expected) => {
expect(normalizeHostHeader(raw)).toBe(expected);
});
@@ -84,7 +85,7 @@ describe("destination interfaces (not remote peer addresses)", () => {
it.each(
(
"127.0.0.1|::1|0:0:0:0:0:0:0:1|::ffff:127.0.0.1|::ffff:7f00:1|" +
- "PII_REDACTED_DEV_IPV4_01|::ffff:PII_REDACTED_DEV_IPV4_01|PII_REDACTED_DEV_IPV6_01"
+ "192.0.2.10|::ffff:192.0.2.10|2001:db8::10"
).split("|"),
)("allows local address %s", (address) => {
expect(isAllowedLocalAddress(address, allowedAddresses)).toBe(true);
@@ -115,10 +116,10 @@ describe("destination interfaces (not remote peer addresses)", () => {
describe("shared HTTP / HMR request decision", () => {
it.each([
undefined,
- "http://PII_REDACTED_DEVICE_01.:34204",
- "https://[PII_REDACTED_DEV_IPV6_01]:443",
+ "http://DEV-NODE.:34204",
+ "https://[2001:db8::10]:443",
])("allows trusted or absent Origin %j", (origin) => {
- expect(request("::ffff:PII_REDACTED_DEV_IPV4_01", "PII_REDACTED_DEVICE_01", origin)).toBe("ok");
+ expect(request("::ffff:192.0.2.10", "dev-node", origin)).toBe("ok");
});
const badOrigins =
"null|file://localhost|https://evil.com|http://localhost/|http://user@localhost";
diff --git a/scripts/probe-tab-PII_REDACTED_CONVERSATION_SHORT_01.ts b/scripts/probe-tab.ts
index 7e2fad7..16ad423 100644
--- a/scripts/probe-tab-PII_REDACTED_CONVERSATION_SHORT_01.ts
+++ b/scripts/probe-tab.ts
@@ -1,8 +1,8 @@
/**
- * Focused probe: load conversation PII_REDACTED_CONVERSATION_SHORT_01 through the FE's REAL pure modules
+ * Focused probe: load a selected conversation through the FE's REAL pure modules
* and see whether the tab's render path would throw. Not part of the test suite.
*
- * bun scripts/probe-tab-PII_REDACTED_CONVERSATION_SHORT_01.ts
+ * bun scripts/probe-tab.ts <conversationId>
*/
import "fake-indexeddb/auto";
import type {
@@ -23,7 +23,11 @@ import {
} from "../src/core/metrics/index.ts";
const HTTP_BASE = process.env.PROBE_HTTP ?? "http://localhost:24991";
-const CONV = "PII_REDACTED_CONVERSATION_01";
+const CONV = process.argv[2];
+if (!CONV) {
+ console.error("Usage: bun scripts/probe-tab.ts <conversationId>");
+ process.exit(1);
+}
async function historySync(id: string, sinceSeq: number): Promise<ConversationHistoryResponse> {
const url = `${HTTP_BASE}/conversations/${encodeURIComponent(id)}?sinceSeq=${sinceSeq}`;
@@ -56,7 +60,11 @@ try {
let state = initialState();
try {
state = applyHistory(state, chunks as never);
- record("applyHistory(145 chunks)", true, `state=${JSON.stringify(state).length} bytes`);
+ record(
+ `applyHistory(${chunks.length} chunks)`,
+ true,
+ `state=${JSON.stringify(state).length} bytes`,
+ );
} catch (e) {
record("applyHistory", false, String(e));
}
diff --git a/scripts/vite-network-guard.test.ts b/scripts/vite-network-guard.test.ts
index 651b635..8bef397 100644
--- a/scripts/vite-network-guard.test.ts
+++ b/scripts/vite-network-guard.test.ts
@@ -5,8 +5,14 @@ import { describe, expect, it, vi } from "vitest";
import { applyViteNetworkPolicy, devNetworkGuard } from "./vite-network-guard";
const policy = {
- allowedHosts: new Set(["localhost", "127.0.0.1", "::1", "node.ts.net", "100.64.0.10"]),
- allowedAddresses: new Set(["127.0.0.1", "::1", "100.64.0.10"]),
+ allowedHosts: new Set([
+ "localhost",
+ "127.0.0.1",
+ "::1",
+ "dev-node.tailnet.example",
+ "192.0.2.10",
+ ]),
+ allowedAddresses: new Set(["127.0.0.1", "::1", "192.0.2.10"]),
};
function install(mode: "dev" | "preview") {
@@ -39,7 +45,13 @@ const cases = [
["loopback", "127.0.0.1", "localhost", undefined, 200],
["IPv6 loopback", "::1", "[::1]:34204", undefined, 200],
["mapped address / canonical Host", "::ffff:127.0.0.1", "LOCALHOST.:34204", undefined, 200],
- ["tailnet", "100.64.0.10", "node.ts.net", "https://node.ts.net:34204", 200],
+ [
+ "tailnet",
+ "192.0.2.10",
+ "dev-node.tailnet.example",
+ "https://dev-node.tailnet.example:34204",
+ 200,
+ ],
["unselected interface with trusted Host", "172.17.0.1", "localhost", undefined, 403],
["missing interface", undefined, "localhost", undefined, 403],
["arbitrary IP Host", "127.0.0.1", "127.0.0.2", undefined, 421],
diff --git a/src/app/resolve-http-url.test.ts b/src/app/resolve-http-url.test.ts
index e544255..c556812 100644
--- a/src/app/resolve-http-url.test.ts
+++ b/src/app/resolve-http-url.test.ts
@@ -19,13 +19,13 @@ describe("resolveHttpUrl", () => {
});
it("derives http://<hostname>:24203 from http location", () => {
- const result = resolveHttpUrl({}, { protocol: "http:", hostname: "PII_REDACTED_DEV_IPV4_01" });
- expect(result).toBe("http://PII_REDACTED_DEV_IPV4_01:24203");
+ const result = resolveHttpUrl({}, { protocol: "http:", hostname: "192.0.2.10" });
+ expect(result).toBe("http://192.0.2.10:24203");
});
it("derives https://<hostname>:24203 from https location", () => {
- const result = resolveHttpUrl({}, { protocol: "https:", hostname: "PII_REDACTED_DEVICE_01" });
- expect(result).toBe("https://PII_REDACTED_DEVICE_01:24203");
+ const result = resolveHttpUrl({}, { protocol: "https:", hostname: "dev-node" });
+ expect(result).toBe("https://dev-node:24203");
});
it("uses VITE_HTTP_PORT when set", () => {
diff --git a/src/app/resolve-ws-url.test.ts b/src/app/resolve-ws-url.test.ts
index ab3a379..be1ab12 100644
--- a/src/app/resolve-ws-url.test.ts
+++ b/src/app/resolve-ws-url.test.ts
@@ -19,13 +19,13 @@ describe("resolveWsUrl", () => {
});
it("derives ws://<hostname>:24205 from http location", () => {
- const result = resolveWsUrl({}, { protocol: "http:", hostname: "PII_REDACTED_DEV_IPV4_01" });
- expect(result).toBe("ws://PII_REDACTED_DEV_IPV4_01:24205");
+ const result = resolveWsUrl({}, { protocol: "http:", hostname: "192.0.2.10" });
+ expect(result).toBe("ws://192.0.2.10:24205");
});
it("derives wss://<hostname>:24205 from https location", () => {
- const result = resolveWsUrl({}, { protocol: "https:", hostname: "PII_REDACTED_DEVICE_01" });
- expect(result).toBe("wss://PII_REDACTED_DEVICE_01:24205");
+ const result = resolveWsUrl({}, { protocol: "https:", hostname: "dev-node" });
+ expect(result).toBe("wss://dev-node:24205");
});
it("uses VITE_WS_PORT when set", () => {
diff --git a/src/features/chat/ui/ChatView.test.ts b/src/features/chat/ui/ChatView.test.ts
index a6bd8ac..157ccac 100644
--- a/src/features/chat/ui/ChatView.test.ts
+++ b/src/features/chat/ui/ChatView.test.ts
@@ -14,7 +14,7 @@ async function openAllCollapses(container: HTMLElement): Promise<void> {
}
// Defensive rendering: a conversation transcript can contain duplicate
-// `toolCallId`s (provider double-emission, persisted — e.g. PII_REDACTED_CONVERSATION_SHORT_01). The
+// `toolCallId`s (persisted provider double-emission). The
// grouped render path MUST dedupe them so the keyed {#each} never throws
// Svelte `each_key_duplicate` (which aborts the reactive flush and makes the
// tab appear dead). See fix-toolcall-duplicate-keys plan Part 1.
@@ -45,7 +45,7 @@ describe("ChatView — duplicate toolCallIds", () => {
},
provisional: false,
},
- // The PII_REDACTED_CONVERSATION_SHORT_01 pattern: the same ids re-emitted immediately after.
+ // The duplicate-emission pattern: the same ids re-emitted immediately after.
{
seq: 3,
role: "assistant",