Track vrek export, MCP config and IDE VCS mapping
Commits .vrek/log.ndjson, the vrek export of goals, principles, decisions, issues and evidence (restore with vrek_import), plus .mcp.json, which wires the vrek MCP server, and .idea/vcs.xml. The SQLite database and its -wal/-shm files are local state and are gitignored; the export is refreshed and committed periodically. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
71d02195ce
commit
97a6b59cfe
4
.gitignore
vendored
4
.gitignore
vendored
@ -43,3 +43,7 @@ next-env.d.ts
|
||||
# camera credentials store (passwords)
|
||||
/.data/
|
||||
/snapshot.jpg
|
||||
|
||||
# vrek: the SQLite database is local state; commit the export (.vrek/log.ndjson) instead
|
||||
/.vrek/*.db
|
||||
/.vrek/*.db-*
|
||||
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
12
.mcp.json
Normal file
12
.mcp.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"vrek": {
|
||||
"command": "vrek-mcp",
|
||||
"env": {
|
||||
"VREK_ACTOR": "claude",
|
||||
"VREK_ACTOR_KIND": "agent",
|
||||
"VREK_MODEL": "claude-opus-5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
201
.vrek/log.ndjson
Normal file
201
.vrek/log.ndjson
Normal file
@ -0,0 +1,201 @@
|
||||
{"id":"evt-4gnef5mwb0p7","type":"principal.created","subject":"prn-cmzz6ey","actor":"prn-cmzz6ey","actor_kind":"human","session":null,"payload":{"id":"prn-cmzz6ey","kind":"human","handle":"unknown","model":null},"at":"2026-09-19T13:17:08.655Z","parents":[],"hash":"0e584dd0f4ecaf44943b98a046324b13ea8254ad742e1d47cb5e22a34068a27a"}
|
||||
{"id":"evt-4m6c69a7ff07","type":"workspace.configured","subject":null,"actor":"prn-cmzz6ey","actor_kind":"human","session":null,"payload":{"name":"cameras"},"at":"2026-09-19T13:17:08.656Z","parents":["evt-4gnef5mwb0p7"],"hash":"b67d67e17571c463cc48d389590627d1c4b2230dc931ccf1f47ddfd231c7c0e3"}
|
||||
{"id":"evt-p29p53002whg","type":"principal.created","subject":"prn-q80g8mz","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"id":"prn-q80g8mz","kind":"agent","handle":"claude","model":"claude-opus-5"},"at":"2026-09-19T13:18:09.387Z","parents":["evt-4m6c69a7ff07"],"hash":"50647a106f7e629b3218bba488066d44905e7dc9176d7d631d499f983fa36b2a"}
|
||||
{"id":"evt-y950xfjmdrpm","type":"node.created","subject":"gol-6q1q5mr","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"goal","title":"Easy-to-use web interface for managing Hisense and Annke cameras","body":"Provide a simple web UI (this Next.js app) to discover, view, and manage Hisense and Annke IP cameras from one place, without needing each vendor's own tools.","status":"active","owner":null,"attrs":{},"weight":null,"target":null,"direction":"up","unit":null},"at":"2026-09-19T13:18:09.389Z","parents":["evt-p29p53002whg"],"hash":"e500905a99b7cb971cf0261772b8ba2a537b460bacd562318301cf2446641155"}
|
||||
{"id":"evt-9rw30xydxg03","type":"node.updated","subject":"gol-6q1q5mr","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"title":"Easy-to-use web interface for managing Hikvision and Annke cameras","body":"Provide a simple web UI (this Next.js app) to discover, view, and manage Hikvision and Annke IP cameras from one place, without needing each vendor's own tools. Annke cameras are largely Hikvision OEM hardware, so both are expected to share the Hikvision ISAPI surface."}},"at":"2026-09-19T13:18:29.271Z","parents":["evt-y950xfjmdrpm"],"hash":"3362e9eeaff74e9091f485c6fa719708893fc27f9d4575dd66d0e929ba28ae59"}
|
||||
{"id":"evt-t662qrse8wf5","type":"node.created","subject":"pri-mz2jxpb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Minimal dependencies: no new package without a recorded, answered justification","body":"The baseline is what package.json holds as of 2026-09-19 — runtime: next, react, react-dom, onvif, server-only; dev: typescript, eslint, eslint-config-next, tailwindcss, @tailwindcss/postcss, @types/*. Anything beyond that (runtime or dev) is not added until a vrek question has been opened with vrek_ask and answered by a human. The question must state: (1) what problem the package solves, (2) why it can't reasonably be done with the platform (Node built-ins, Web APIs, Next.js/React features) or code already here, (3) size / transitive dependency count and whether it is server-only or ships to the client, (4) maintenance and security health (last release, open advisories, license), (5) how hard it would be to remove later. Why: every dependency is attack surface on a box that holds camera credentials, adds upgrade churn, and this app's job (talk ONVIF/ISAPI over HTTP, render a UI) is well covered by fetch, crypto and Next itself. Rules out: installing a package 'to try it', pulling a library for a helper that is a few lines of code, and adding a UI component kit. Upgrading an existing package's version is not a new dependency.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:20:37.601Z","parents":["evt-9rw30xydxg03"],"hash":"f76135bece320651018a7d8bcf3fc71b1844e2430d17364d1b2bc823df710bac"}
|
||||
{"id":"evt-hw63e1h6hsq0","type":"edge.added","subject":"pri-mz2jxpb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-mz2jxpb","to":"gol-6q1q5mr"},"at":"2026-09-19T13:20:37.603Z","parents":["evt-t662qrse8wf5"],"hash":"4555857423c119c5a51e931b135617297d9f59539cdb88a39ea68d0a8f6a8335"}
|
||||
{"id":"evt-37er6eht710q","type":"node.created","subject":"pri-p9h51hx","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Modular code: small single-purpose modules with one-way dependencies, vendor quirks behind a common camera interface","body":"Layers, top to bottom, each depending only on the ones below it: (1) UI — src/app pages and components; (2) entry points — src/app/api/**/route.ts and Server Actions, kept thin: parse and validate input, call one lib function, map the result or error to a response (as info/route.ts does today via camera-route.ts); (3) domain — src/lib modules, one responsibility each: discovery (onvif.ts), device protocol (camera.ts), camera registry (camera-registry.ts), credential store (credential-store.ts), HTTP glue (camera-route.ts); (4) platform — Node built-ins and the few allowed packages. Vendor-specific behaviour (Hikvision ISAPI endpoints, Annke firmware quirks) lives in its own module behind a shared CameraTarget/CameraInfo-style interface, so a page or route never branches on brand. A module exports types and functions, not mutable state; errors are typed classes (CameraAuthError, CredentialStoreError) that the route layer maps to HTTP status. No import cycles, no reaching into another module's private helpers, and nothing in src/lib imports from src/app. A file that grows a second responsibility gets split. Why: device protocols are the part most likely to change or grow (ISAPI, PTZ, recording, new models); keeping them isolated means a new camera feature is a new module plus a thin route, not an edit across the UI. Rules out: business logic in route handlers or components, one big 'utils' file, and vendor checks scattered through the UI.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:22:42.461Z","parents":["evt-hw63e1h6hsq0"],"hash":"f92df5ca42bc6b1dc444b1589e32ca30abec0c959f73fd8f7fe96bb761debf75"}
|
||||
{"id":"evt-5yncr2v9h2qx","type":"edge.added","subject":"pri-p9h51hx","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-p9h51hx","to":"gol-6q1q5mr"},"at":"2026-09-19T13:22:42.463Z","parents":["evt-37er6eht710q"],"hash":"fb27b46e1478c5c4b02c7c67851dcd533ed0275fbfd22d3129738b729d2df771"}
|
||||
{"id":"evt-98rz8qgb6hyy","type":"node.created","subject":"pri-e0qjyy0","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Write Next.js code against the bundled v16 docs (node_modules/next/dist/docs), not memory of older versions","body":"The installed next is 16.3.5, and the docs that match it ship in node_modules/next/dist/docs. Read the relevant page before writing Next-specific code. Next 16 rules that older habits break (source: 02-guides/upgrading/version-16.md unless noted): params, searchParams, cookies() and headers() are Promises and must be awaited; type route handlers with the global RouteContext<'/api/...'> helper. Request interception is src/proxy.ts exporting proxy; middleware.ts is deprecated, and proxy is for coarse checks, not authorization (01-getting-started/16-proxy.md). The edge runtime is deprecated, so don't export runtime='edge'. Turbopack is the default and a custom webpack config fails the build. revalidateTag takes (tag, profile); updateTag and refresh() exist for Server Actions. unstable_cache is replaced by 'use cache', which requires cacheComponents, and that is off here. unstable_noStore is replaced by connection(). serverRuntimeConfig and publicRuntimeConfig are removed; read env at runtime after await connection(). next lint is removed, so run eslint directly. next/image blocks local IPs by default. Why: training data and blog posts describe Next 13–15; code written from them compiles poorly or uses removed APIs. Rules out: middleware.ts, synchronous params access, unstable_cache, and edge runtime exports.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:22:48.194Z","parents":["evt-5yncr2v9h2qx"],"hash":"7fded33ab649be556cdf36b9659a280000aa67a629ec7ac857f000f28553f566"}
|
||||
{"id":"evt-paffx15pwdkd","type":"edge.added","subject":"pri-e0qjyy0","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-e0qjyy0","to":"gol-6q1q5mr"},"at":"2026-09-19T13:22:48.195Z","parents":["evt-98rz8qgb6hyy"],"hash":"d96db3a8657808d0f5d92fe682f3e3039e2889722bea9705ba19dbe934afad50"}
|
||||
{"id":"evt-n3wssys9zfmf","type":"node.created","subject":"pri-tyrxdz9","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Camera credentials and device access stay on the server; the client only ever sees minimal DTOs","body":"Every src/lib module that touches devices, credentials, the registry or process.env starts with import 'server-only', so importing it into a Client Component is a build error. Client code may import types only (import type). Routes and Server Actions return only what the UI needs: never a password, never a raw device record or SOAP/ISAPI response, never an upstream error message verbatim, since those can echo URLs with user:pass. Secrets are non-NEXT_PUBLIC_ env vars (e.g. CAMERA_CREDENTIALS_KEY) kept in .env* at the project root and out of git; stored credentials stay encrypted at rest in .data/ (gitignored). Why: the server holds the logins for every camera on the network, and props, action return values and error bodies are all serialized to the browser. Sources: 02-guides/data-security.md ('Data Access Layer', 'Controlling return values', 'Preventing client-side execution of server-only code'); 02-guides/environment-variables.md; 02-guides/backend-for-frontend.md ('Working with headers'). Rules out: an API that returns a password, even to 'pre-fill' a form; NEXT_PUBLIC_ secrets; and passing lib objects straight into client props.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:22:52.351Z","parents":["evt-paffx15pwdkd"],"hash":"3fdca55b4cf156d12738d48ccf71f448449290937a23332eea8494d18f3e2939"}
|
||||
{"id":"evt-b3xyagkdfnjk","type":"edge.added","subject":"pri-tyrxdz9","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-tyrxdz9","to":"gol-6q1q5mr"},"at":"2026-09-19T13:22:52.353Z","parents":["evt-n3wssys9zfmf"],"hash":"e3c2d213cb1bf774055ea91fa602eb791689adeb94992345715f0f1b26c98773"}
|
||||
{"id":"evt-j4tn6nn59and","type":"node.created","subject":"pri-m1csgrm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Every route handler and Server Action is a public, untrusted entry point: validate input and only talk to known private cameras","body":"Next's docs are explicit: Route Handlers are public HTTP endpoints, and Server Functions are reachable by direct POST, not just through the UI. Being LAN-only doesn't change that. So each entry point validates its own input: the [id] param, the JSON body, and FormData fields for type, length and format. Validation is small hand-written checks in the lib layer, with no schema library (see the minimal-dependencies principle). The device address is always resolved server-side from the camera registry by id and must pass isAllowedHost (private IPv4); a client-supplied host or URL is never fetched, which prevents SSRF. Every outbound ONVIF/ISAPI call has a timeout. Why: this server can reach every device on the LAN with stored admin logins, so an unvalidated endpoint is a pivot into the network. Sources: 02-guides/backend-for-frontend.md ('Public Endpoints', 'Verify payloads', 'Proxying to a backend'); 02-guides/server-actions.md ('Security'); 02-guides/data-security.md ('Validating client input'). Rules out: accepting host or port from the request, trusting that a request came from our own UI, and device calls with no timeout.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:22:57.221Z","parents":["evt-b3xyagkdfnjk"],"hash":"e371efae71f617d7493fe4d2042f63db6259c45ad084ba901c47f7788a355f59"}
|
||||
{"id":"evt-z4eev123b4a3","type":"edge.added","subject":"pri-m1csgrm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-m1csgrm","to":"gol-6q1q5mr"},"at":"2026-09-19T13:22:57.223Z","parents":["evt-j4tn6nn59and"],"hash":"d42a599e928f814ffef62557e5d7c1604f553bd2f98f5b256f944fe3418b939b"}
|
||||
{"id":"evt-7ccp8csngzyx","type":"node.created","subject":"pri-01np850","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Server Components by default, 'use client' only on small interactive leaves; server code calls src/lib directly, never our own /api","body":"Pages and layouts are Server Components that read data by calling src/lib functions directly. Route Handlers exist for Client Components, e.g. snapshot refresh or polling, not for server-to-server calls to ourselves. 'use client' goes on the smallest component that needs state, effects or event handlers, because everything a client file imports ships to the browser. Only serializable props cross the boundary. Slow per-camera reads are fetched in parallel (Promise.allSettled, so one dead camera doesn't fail the page) and wrapped in <Suspense> so they stream. Client polling uses plain fetch in an effect, with no SWR or React Query unless a dependency question justifies one. Why: this keeps the client bundle and the attack surface small and pages fast when some cameras are offline. Sources: 01-getting-started/05-server-and-client-components.md ('Reducing JS bundle size'); 02-guides/server-and-client-boundary.md; 02-guides/backend-for-frontend.md ('Caveats > Server Components'); 01-getting-started/06-fetching-data.md ('Streaming', 'Parallel data fetching'). Rules out: 'use client' at page level by reflex and server components fetching http://localhost/api/....","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:23:01.925Z","parents":["evt-z4eev123b4a3"],"hash":"cc4c0c5d19f1694aad4824bfd3e21d1135c6f6d82f3abec557aa411aaac51f11"}
|
||||
{"id":"evt-ce37bvhnb4k2","type":"edge.added","subject":"pri-01np850","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-01np850","to":"gol-6q1q5mr"},"at":"2026-09-19T13:23:01.926Z","parents":["evt-7ccp8csngzyx"],"hash":"c7a1cf9eeeecacffccbe906d108f1c92003b838180c061fe8c1554aa850c1ee8"}
|
||||
{"id":"evt-2r7e4hajwf8v","type":"node.created","subject":"pri-qqrp49f","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"principle","title":"Live device state is never cached; mutations go through Server Actions that return expected errors as values","body":"Camera status, snapshots and settings read from a device are always fetched fresh. Route Handlers and fetch are uncached by default in Next 16 with cacheComponents off, so don't opt them into caching. React.cache is fine for de-duplicating within a single request. Changes to a device or to stored credentials go through Server Actions (or POST/PUT route handlers when a Client Component must call them imperatively). Expected failures, like a wrong password, an unreachable camera or an unsupported feature, come back as return values shown via useActionState, not thrown. Unexpected errors fall through to error.tsx. After a mutation, call refresh() or revalidatePath so the UI re-reads the device. Actions run one at a time per client, so they are not used for reads or fan-out. Why: a stale view of a security camera is worse than a slow one, and users need to know why a change failed. Sources: 01-getting-started/15-route-handlers.md ('Caching'); 01-getting-started/10-error-handling.md ('Handling expected errors'); 02-guides/server-actions.md ('Sequential dispatch', 'Choosing a cache update'); 01-getting-started/07-mutating-data.md. Rules out: 'use cache' or revalidate timers on device data, and using Server Actions to fetch.","status":"active","owner":"prn-q80g8mz","attrs":{},"weight":null},"at":"2026-09-19T13:23:07.390Z","parents":["evt-ce37bvhnb4k2"],"hash":"25affdb731f0b0b9de1544af9c88d9a722b7a1cf92f931221c4c923bcf120e65"}
|
||||
{"id":"evt-hzgt5xtm5vdj","type":"edge.added","subject":"pri-qqrp49f","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-qqrp49f","to":"gol-6q1q5mr"},"at":"2026-09-19T13:23:07.391Z","parents":["evt-2r7e4hajwf8v"],"hash":"87b0716e6168dfa876648077dde07fff396c3dfaf65f2718983e99b8316b31f9"}
|
||||
{"id":"evt-hzehfrtjnrzz","type":"node.created","subject":"doc-e0nsr0v","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"doc","title":"Coding guide","body":"How code in this repo is written. The rules are the workspace's active principles, listed live below, so this guide can't drift from them. To change a rule, add, update or retire a principle; don't edit this page. Next.js specifics come from the version-matched docs in node_modules/next/dist/docs (next 16.3.5).","status":"active","owner":"prn-q80g8mz","attrs":{"blocks":[{"type":"text","text":"Before writing code: read these principles, and the bundled Next.js doc page for any Next API you touch. Adding a package needs an answered vrek question first.","by":"prn-q80g8mz"},{"type":"query","query":{"kind":"principle","status":"active"},"by":"prn-q80g8mz"}],"acknowledged_at_seq":null}},"at":"2026-09-19T13:23:15.029Z","parents":["evt-hzgt5xtm5vdj"],"hash":"a826220db53e93531025a319bb38b2cbfc1e786bb2927085c30a2bc982cf1d11"}
|
||||
{"id":"evt-4mye2zcxvw4a","type":"edge.added","subject":"doc-e0nsr0v","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"covers","from":"doc-e0nsr0v","to":"gol-6q1q5mr"},"at":"2026-09-19T13:23:15.030Z","parents":["evt-hzehfrtjnrzz"],"hash":"365fff6be3ccf625c4d035a0723f77d0435c82c06b15517503729b78218887c2"}
|
||||
{"id":"evt-bq6m9vmr6q6x","type":"node.created","subject":"que-prbknze","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"question","title":"Add zod as a runtime dependency for validating route and Server Action input?","body":"Dependency justification, as pri-mz2jxpb requires. (1) Problem: every route handler and Server Action must validate [id] params, JSON bodies and FormData (pri-m1csgrm), and the Next 16 docs' own examples use zod for this (02-guides/forms.md, data-security.md). (2) Why not hand-written: hand-written checks work, but schemas give one declarative definition per input, with typed output via z.infer and consistent error messages; hand-rolling that across every endpoint duplicates code. (3) Size and reach: zod 4.6.5, MIT, zero runtime dependencies. It is already in node_modules transitively (eslint-config-next → eslint-plugin-react-hooks), so installing adds no new code to the tree. It is used server-side; it only ships to the client if a client component imports a schema. (4) Health: actively maintained, latest release 4.6.5, registry updated 2026-09-13. (5) Removal: schemas sit in the lib/entry layer only, so swapping them for hand-written checks later is local.","status":"open","owner":null,"attrs":{"options":[{"option":"Add zod","consequence":"Input validation uses zod schemas; pri-m1csgrm is updated to name it."},{"option":"Don't add","consequence":"Validation stays hand-written in src/lib."}],"revisit":null}},"at":"2026-09-19T13:28:08.909Z","parents":["evt-4mye2zcxvw4a"],"hash":"75421c3cb917ade396bd4ea32af54aa9dfbdf22ce96c54503503af803d9159e4"}
|
||||
{"id":"evt-jxbx4dggh0c6","type":"edge.added","subject":"que-prbknze","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"que-prbknze","to":"gol-6q1q5mr"},"at":"2026-09-19T13:28:08.910Z","parents":["evt-bq6m9vmr6q6x"],"hash":"3efe61fc9fe22addf2ef448c01114a24adc39ed6aaf83a031c59707ddca93b0e"}
|
||||
{"id":"evt-yymjyzvqsfr1","type":"edge.added","subject":"pri-mz2jxpb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-mz2jxpb","to":"que-prbknze"},"at":"2026-09-19T13:28:08.911Z","parents":["evt-jxbx4dggh0c6"],"hash":"05524cc1dbd4f0cd000b66ba671f4a9997dcee800ab19d34ffd0411fca40de32"}
|
||||
{"id":"evt-bxhvfre330xz","type":"edge.added","subject":"pri-m1csgrm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-m1csgrm","to":"que-prbknze"},"at":"2026-09-19T13:28:08.912Z","parents":["evt-yymjyzvqsfr1"],"hash":"8b169d43d4752636b3e1e3fa4d89d2f3205d8a34f59261eef99d1e468b29c4b8"}
|
||||
{"id":"evt-wzt776dwyqsz","type":"node.created","subject":"dec-jfrds1r","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"decision","title":"Accept zod as a runtime dependency for input validation","body":"Accepted by the user (Michael Mainguy) in conversation on 2026-09-19: \"I accept zod as a new dependency.\" Rejected alternative: hand-written validators in src/lib. Scope: validating input at route handlers and Server Actions (pri-m1csgrm). Schemas live server-side; client components don't import them unless deliberately sharing a form schema.","status":"recorded","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:28:13.267Z","parents":["evt-bxhvfre330xz"],"hash":"3cdcfc53a095dc5179c16784101778182099db12fc22284ddef84764408b727e"}
|
||||
{"id":"evt-qxaa2xakyq8z","type":"edge.added","subject":"dec-jfrds1r","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"answers","from":"dec-jfrds1r","to":"que-prbknze"},"at":"2026-09-19T13:28:13.268Z","parents":["evt-wzt776dwyqsz"],"hash":"eabfc130338918159b53d0378c92f2fe3f3d87fef6b4ebedc4655f9042365487"}
|
||||
{"id":"evt-3p1c7h9d742h","type":"question.answered","subject":"que-prbknze","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"answer":"dec-jfrds1r"},"at":"2026-09-19T13:28:13.269Z","parents":["evt-qxaa2xakyq8z"],"hash":"39980da9b7331c634fde44c99b8d4e58564b978c87b2276635eb8e780b29bf48"}
|
||||
{"id":"evt-8cjj740y7psa","type":"edge.added","subject":"dec-jfrds1r","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"dec-jfrds1r","to":"gol-6q1q5mr"},"at":"2026-09-19T13:28:13.270Z","parents":["evt-3p1c7h9d742h"],"hash":"6ce58f1a24d4a2b1b61807ae054bfc0b450df4bb53e689902387b52d9a0c4ec0"}
|
||||
{"id":"evt-6jxj3bk4fjv7","type":"node.updated","subject":"pri-m1csgrm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"body":"Next's docs are explicit: Route Handlers are public HTTP endpoints, and Server Functions are reachable by direct POST, not just through the UI. Being LAN-only doesn't change that. So each entry point validates its own input with a zod schema: the [id] param, the JSON body, and FormData fields. It uses safeParse and turns failures into a 400, or into a returned error value for actions. zod was accepted as a dependency on 2026-09-19 (que-prbknze). The device address is always resolved server-side from the camera registry by id and must pass isAllowedHost (private IPv4); a client-supplied host or URL is never fetched, which prevents SSRF. Every outbound ONVIF/ISAPI call has a timeout. Why: this server can reach every device on the LAN with stored admin logins, so an unvalidated endpoint is a pivot into the network. Sources: 02-guides/backend-for-frontend.md ('Public Endpoints', 'Verify payloads', 'Proxying to a backend'); 02-guides/server-actions.md ('Security'); 02-guides/data-security.md ('Validating client input'); 02-guides/forms.md ('Validation errors'). Rules out: accepting host or port from the request, trusting that a request came from our own UI, and device calls with no timeout."}},"at":"2026-09-19T13:28:16.723Z","parents":["evt-8cjj740y7psa"],"hash":"84ad70eb5f9874a21f9b8a2af57c797ab3b1abdae899021241a8c5f5cc5d2b63"}
|
||||
{"id":"evt-m6srta3gzyr7","type":"node.created","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"goal","title":"Multi-camera dashboard: server does the work, the browser is a thin display layer","body":"One dashboard showing every known Hikvision/Annke camera at once: live status, snapshot and key settings, with changes made from the same screen. As much as possible runs on the server: discovery, device protocol, credential use, validation, data shaping and error mapping, in src/lib, Server Components, route handlers and Server Actions. The client is a thin wrapper that renders what the server returns and keeps it fresh (React Query for polling, dedupe and invalidation). No device logic, protocol knowledge or business rules live in client components. Why: one screen is the easy-to-use part of the parent goal, and keeping logic server-side keeps credentials off the browser and makes the client trivially replaceable.","status":"active","owner":null,"attrs":{},"weight":null,"target":null,"direction":"up","unit":null},"at":"2026-09-19T13:30:56.600Z","parents":["evt-6jxj3bk4fjv7"],"hash":"0ab4c61b6907eee8b59b4cf8ae81fde70f5ee1c435b494ff4874387437b76435"}
|
||||
{"id":"evt-vv0xvpvv6zfx","type":"edge.added","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"gol-146a3cb","to":"gol-6q1q5mr"},"at":"2026-09-19T13:30:56.601Z","parents":["evt-m6srta3gzyr7"],"hash":"68e5f155c75dfd32ff903f4ab9a2af0062ff0159f69badec370738eca0a497d9"}
|
||||
{"id":"evt-85tshfa63ytf","type":"edge.added","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-146a3cb","to":"pri-01np850"},"at":"2026-09-19T13:30:56.602Z","parents":["evt-vv0xvpvv6zfx"],"hash":"0b301286c71f915b7fb145c209a087538d4ee57d3dd8ac9afd7f84fb897d00a1"}
|
||||
{"id":"evt-0mwt26qccg1k","type":"edge.added","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-146a3cb","to":"pri-tyrxdz9"},"at":"2026-09-19T13:30:56.603Z","parents":["evt-85tshfa63ytf"],"hash":"04c6e77da29e0926083659938078ffccdb3254a3a16b17efe59c794f01e25317"}
|
||||
{"id":"evt-010bwknvqz8z","type":"edge.added","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-146a3cb","to":"pri-p9h51hx"},"at":"2026-09-19T13:30:56.604Z","parents":["evt-0mwt26qccg1k"],"hash":"0f81bf9681ed97070454e08844ad6e70b3ce1354cfd2d0888929c64ace1b22a6"}
|
||||
{"id":"evt-nbdfdvdwr56z","type":"node.created","subject":"que-m9zjfbk","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"question","title":"Add @tanstack/react-query as a runtime dependency for client-side polling and cache invalidation on the multi-camera dashboard?","body":"Dependency justification, as pri-mz2jxpb requires. (1) Problem: the multi-camera dashboard polls status and snapshots for many cameras from the browser, and after a change it must refresh exactly the affected camera. camera-card.tsx already hand-rolls this with useState, useEffect and an attempt counter, and that code would repeat per camera and per widget. (2) Why not the platform: Server Components handle the initial render, but live polling has to happen in the client. React Query gives keyed dedupe, refetchInterval that pauses in hidden tabs, retry control and invalidateQueries in one tested place; rebuilding it by hand amounts to writing a small query library. (3) Size and reach: @tanstack/react-query 5.103.1, MIT, one dependency (@tanstack/query-core), peer react ^18 || ^19. It ships to the client, and that is its purpose. Client only: it is never imported from src/lib. (4) Health: actively maintained, registry updated 2026-09-16. (5) Removal: confined to a providers.tsx wrapper plus thin hooks that call our route handlers; the server side is unaffected. Configuration required: retry off or limited for device calls (don't hammer offline cameras), and staleTime 0 for device state (pri-qqrp49f).","status":"open","owner":null,"attrs":{"options":[{"option":"Add @tanstack/react-query","consequence":"Client polling and invalidation go through React Query; pri-01np850 is updated to name it."},{"option":"Don't add","consequence":"Client polling stays hand-written fetch plus useEffect."}],"revisit":null}},"at":"2026-09-19T13:31:03.437Z","parents":["evt-010bwknvqz8z"],"hash":"2a84077c71d45acb9498c101a59a372299807a3a08394c49a93823c402d7e6a3"}
|
||||
{"id":"evt-w405pzy5qyma","type":"edge.added","subject":"que-m9zjfbk","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"que-m9zjfbk","to":"gol-6q1q5mr"},"at":"2026-09-19T13:31:03.439Z","parents":["evt-nbdfdvdwr56z"],"hash":"00950c5b90d025e4bfebb9057963d13d17f3727f58c03363a547391965a94648"}
|
||||
{"id":"evt-3aznwgtd39gn","type":"edge.added","subject":"pri-mz2jxpb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-mz2jxpb","to":"que-m9zjfbk"},"at":"2026-09-19T13:31:03.440Z","parents":["evt-w405pzy5qyma"],"hash":"df7443b854b2083b2bf9143e8a46ad25d337e66d8b83e033ecf39c27904476cc"}
|
||||
{"id":"evt-18ds9jgaa1dp","type":"edge.added","subject":"pri-01np850","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-01np850","to":"que-m9zjfbk"},"at":"2026-09-19T13:31:03.441Z","parents":["evt-3aznwgtd39gn"],"hash":"7476c65f7c4b32e7392b1969f2b68db23721d3bdde2e43c59c6dfe4cb4ce5cfd"}
|
||||
{"id":"evt-s4cd65cmnz3m","type":"edge.added","subject":"pri-qqrp49f","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-qqrp49f","to":"que-m9zjfbk"},"at":"2026-09-19T13:31:03.442Z","parents":["evt-18ds9jgaa1dp"],"hash":"50401fd609c3b724f006f26c60a2bc5a5ca3ce4da5c15cea5d1489e67f394318"}
|
||||
{"id":"evt-harmqb674m2t","type":"node.created","subject":"dec-7qhxvts","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"decision","title":"Accept @tanstack/react-query as the client-side data layer for the dashboard","body":"Accepted by the user (Michael Mainguy) on 2026-09-19: \"go ahead and add react query, the point is to build a multi-camera dashboard and shift as much as possible to server with client being only a thin wrapper.\" Rejected alternative: hand-written fetch plus useEffect polling per component. Scope: client components only, calling our own route handlers. It holds no device logic. Defaults: staleTime 0, limited retry, polling pauses in hidden tabs.","status":"recorded","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:31:08.446Z","parents":["evt-s4cd65cmnz3m"],"hash":"89cb7736ca1ead353c63c44861a49c23f3542be59668ca41887da39ef036d2cb"}
|
||||
{"id":"evt-wrz2yyk59bag","type":"edge.added","subject":"dec-7qhxvts","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"answers","from":"dec-7qhxvts","to":"que-m9zjfbk"},"at":"2026-09-19T13:31:08.447Z","parents":["evt-harmqb674m2t"],"hash":"999d8850b82ea37b57a98a7c7d007e2215b6bf665de6af8240b8b3f92e1de8c0"}
|
||||
{"id":"evt-p3f3p9nwckyw","type":"question.answered","subject":"que-m9zjfbk","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"answer":"dec-7qhxvts"},"at":"2026-09-19T13:31:08.448Z","parents":["evt-wrz2yyk59bag"],"hash":"8e4473df470e33e7e35080c8f4c2c86a1bdad90e0a8e83d4ef805a14e9560759"}
|
||||
{"id":"evt-ns7dz8z369pp","type":"edge.added","subject":"dec-7qhxvts","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"dec-7qhxvts","to":"gol-146a3cb"},"at":"2026-09-19T13:31:08.449Z","parents":["evt-p3f3p9nwckyw"],"hash":"32805a2901b2945e2d046df02413eb2a64515b0fb1b1aa8a456d37dd7dcdb2c4"}
|
||||
{"id":"evt-xn4se6xetcrf","type":"edge.added","subject":"dec-7qhxvts","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"dec-7qhxvts","to":"gol-6q1q5mr"},"at":"2026-09-19T13:31:08.450Z","parents":["evt-ns7dz8z369pp"],"hash":"bb0fcf37677b85a4ba0a33b3d529c5149f14592eed169a5a50c8d2c406356ab1"}
|
||||
{"id":"evt-wh3sekj42gxm","type":"node.updated","subject":"pri-01np850","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"body":"Pages and layouts are Server Components that read data by calling src/lib functions directly. Route Handlers exist for Client Components, e.g. snapshot refresh or polling, not for server-to-server calls to ourselves. 'use client' goes on the smallest component that needs state, effects or event handlers, because everything a client file imports ships to the browser. Only serializable props cross the boundary. Slow per-camera reads are fetched in parallel (Promise.allSettled, so one dead camera doesn't fail the page) and wrapped in <Suspense> so they stream. Client-side reads and polling go through @tanstack/react-query (accepted 2026-09-19, que-m9zjfbk). Query functions only call our own route handlers and contain no device logic. Keys are ['camera', id, ...] so a mutation can invalidate one camera. Why: this keeps the client bundle and the attack surface small and pages fast when some cameras are offline. Sources: 01-getting-started/05-server-and-client-components.md ('Reducing JS bundle size', 'Context providers'); 02-guides/server-and-client-boundary.md; 02-guides/backend-for-frontend.md ('Caveats > Server Components'); 01-getting-started/06-fetching-data.md ('Streaming', 'Parallel data fetching'). Rules out: 'use client' at page level by reflex, server components fetching http://localhost/api/..., and hand-rolled useEffect polling."}},"at":"2026-09-19T13:31:11.869Z","parents":["evt-xn4se6xetcrf"],"hash":"c55327598ee2c255ef1252798c9adb228f358337070a3ab3d9a7d83d2e18c793"}
|
||||
{"id":"evt-bj1hp8jppa8r","type":"node.created","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Migrate camera UI to a server-rendered multi-camera dashboard with a thin React Query client","body":"Today page.tsx renders a client-only CameraScanner. Cameras appear only after a scan (client state), and camera-card.tsx hand-rolls info, snapshot and credentials fetching with useState, useEffect and an attempt counter. Target: the page is a Server Component that lists known cameras from the server registry. Client components only render and refresh data through React Query hooks that call our route handlers, and inputs are validated with zod. Done when every child is closed, no client component contains hand-rolled fetch or useEffect polling, and page.tsx has no 'use client' ancestor above the per-camera leaves.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:25.210Z","parents":["evt-wh3sekj42gxm"],"hash":"b9891f935256fa03a0be32775df7679cc36e9d72f8dcbc35bf015b4cab931a23"}
|
||||
{"id":"evt-n8h164qxhthn","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-qbh3541","to":"gol-146a3cb"},"at":"2026-09-19T13:32:25.212Z","parents":["evt-bj1hp8jppa8r"],"hash":"5043eaf2402dc1ab1e002d39298e495c5f15472893fe8cb84c5fa4161c27ad8c"}
|
||||
{"id":"evt-7vjqhew52agq","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-qbh3541","to":"gol-6q1q5mr"},"at":"2026-09-19T13:32:25.213Z","parents":["evt-n8h164qxhthn"],"hash":"8a3fd66af60971eff214f45c46aacf49bf563d730f79ed167c6759c580a9574e"}
|
||||
{"id":"evt-yx7zx5xbrjr8","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-qbh3541","to":"src/app/page.tsx"},"at":"2026-09-19T13:32:25.214Z","parents":["evt-7vjqhew52agq"],"hash":"02153cd1d188ef89b6fe67786bc4a376d248875615726015206257ce913bff0c"}
|
||||
{"id":"evt-efxgker80c3y","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-qbh3541","to":"src/app/camera-scanner.tsx"},"at":"2026-09-19T13:32:25.215Z","parents":["evt-yx7zx5xbrjr8"],"hash":"e6f60b94d64416f28debaa9b54569fb389d9cb4d51d77ecd95d181a4ae0abad7"}
|
||||
{"id":"evt-mnb3x4spazf5","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-qbh3541","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:32:25.216Z","parents":["evt-efxgker80c3y"],"hash":"5854f1849862d88c0107396c78264b4e039d134662b424f9235782da6cf5d6c0"}
|
||||
{"id":"evt-g0az0dgqen16","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-qbh3541","to":"src/lib/camera-registry.ts"},"at":"2026-09-19T13:32:25.217Z","parents":["evt-mnb3x4spazf5"],"hash":"36bf189a9365c67a977de40ce78cb94338bd38c75664cf31e4e48741dd7af177"}
|
||||
{"id":"evt-canp029vey0s","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-qbh3541","to":"area:ui"},"at":"2026-09-19T13:32:25.218Z","parents":["evt-g0az0dgqen16"],"hash":"878e4055c40d3c4ae4952fe6024188c7287dce393704c79f3b563448bc2fef0c"}
|
||||
{"id":"evt-5x26rrcvq478","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-qbh3541","to":"area:dashboard"},"at":"2026-09-19T13:32:25.219Z","parents":["evt-canp029vey0s"],"hash":"255d51c7b86a4376546b37744d862125ab237f4788c49c47ec1b18b728bf14b8"}
|
||||
{"id":"evt-m4gdbhc211pf","type":"node.created","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Render the dashboard page as a Server Component listing known cameras from the registry","body":"Add a listCameras() to src/lib/camera-registry.ts that returns a client-safe DTO per camera (id, name, host, port, lastSeen, hasCredentials). page.tsx calls it directly (no fetch to /api) and renders one client camera tile per record, so known cameras appear on load without a scan. Define the DTO type in a module client code can import with `import type`. Acceptance: with a populated .data/cameras.json, the page shows all cameras before any scan, and page.tsx has no 'use client'.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:30.973Z","parents":["evt-5x26rrcvq478"],"hash":"1f5c14c5e3430145946359df9b116fdf56d94ec28537c8f1b2e49dbb0c35b862"}
|
||||
{"id":"evt-jqc49z6ae4vp","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-a0hz0py","to":"gol-146a3cb"},"at":"2026-09-19T13:32:30.976Z","parents":["evt-m4gdbhc211pf"],"hash":"2dbddc3d6411974e2639fe4cfcf3824a0d11e4fa49e47abddaedec8c10805f0d"}
|
||||
{"id":"evt-zdrhfnrxzrsb","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-a0hz0py","to":"src/app/page.tsx"},"at":"2026-09-19T13:32:30.977Z","parents":["evt-jqc49z6ae4vp"],"hash":"7614aa0b3a649bd60af8219738094a9eb19ead2ebc29c52119c83581d85599a9"}
|
||||
{"id":"evt-fn78qthkd0e3","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-a0hz0py","to":"src/lib/camera-registry.ts"},"at":"2026-09-19T13:32:30.978Z","parents":["evt-zdrhfnrxzrsb"],"hash":"25dfe36fb525bb0d896f114d2f94032332fd1ed45067e4ff98e7f71c534e8424"}
|
||||
{"id":"evt-s5x9a3mncwt6","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-a0hz0py"},"at":"2026-09-19T13:32:30.979Z","parents":["evt-fn78qthkd0e3"],"hash":"bff05b6fcce77eeb8004cbc84151ceab160bde9b1d9b0d1f5375d8b48642d25e"}
|
||||
{"id":"evt-79vjabrmdk1t","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-a0hz0py","to":"area:dashboard"},"at":"2026-09-19T13:32:30.980Z","parents":["evt-s5x9a3mncwt6"],"hash":"14588eb10fb82dfb082ae6c3509e101f693b444a916e2dab8cda59a2a2e65b0f"}
|
||||
{"id":"evt-jd4xemnve4w2","type":"node.created","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Validate all route handler input with zod schemas","body":"Replace the hand-rolled checks with zod safeParse: the [id] param (isValidCameraId in camera-route.ts), the PUT /api/cameras/[id]/credentials body (username required, both strings, sensible max lengths), and the POST /api/discover body (timeout clamped 1–30 s, unicastSweep boolean). Put schemas in src/lib next to what they validate. A failure returns 400 with a generic message. Per pri-m1csgrm.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:34.293Z","parents":["evt-79vjabrmdk1t"],"hash":"9143ee4c6a2461848d0b27915563aef851c6c4f2cfc6c9b63c92a07697221e34"}
|
||||
{"id":"evt-h85t4x4x7tjn","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-rjqy3hy","to":"gol-6q1q5mr"},"at":"2026-09-19T13:32:34.294Z","parents":["evt-jd4xemnve4w2"],"hash":"480a22b6b4cdc9fc1ae34a989c1ab53d0938b2603c705ab18651b0cd9826cd93"}
|
||||
{"id":"evt-b78sv8p32110","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-rjqy3hy","to":"src/lib/camera-route.ts"},"at":"2026-09-19T13:32:34.295Z","parents":["evt-h85t4x4x7tjn"],"hash":"1c5e295ecd44b482eda112f29d790c8de296ce0d7bf39ada4f1f55d0433fda73"}
|
||||
{"id":"evt-pz7ndaxk01xw","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-rjqy3hy","to":"src/app/api/cameras/[id]/credentials/route.ts"},"at":"2026-09-19T13:32:34.296Z","parents":["evt-b78sv8p32110"],"hash":"e5fdd0e2e7ec6b6d26dd7286ce36b9d29ee4de78ec5a25e02bdc86b81da04fde"}
|
||||
{"id":"evt-da99bcqnvg82","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-rjqy3hy","to":"src/app/api/discover/route.ts"},"at":"2026-09-19T13:32:34.297Z","parents":["evt-pz7ndaxk01xw"],"hash":"d8c20c8d682a8ccfddeaf74d67031f9fd18c7cb1c3f4ef2a4b9043bcb1e2f53e"}
|
||||
{"id":"evt-bs1qk9pwt6n8","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-rjqy3hy"},"at":"2026-09-19T13:32:34.298Z","parents":["evt-da99bcqnvg82"],"hash":"5a8fbe4ba2b81177da92361d1262d3d2ad82ab2fda4b897f06173acbf01b1130"}
|
||||
{"id":"evt-pd484j5j3hq5","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-rjqy3hy","to":"area:api"},"at":"2026-09-19T13:32:34.299Z","parents":["evt-bs1qk9pwt6n8"],"hash":"a9abb91ad00c8327224db8d4adb267efa45d45f75fff36b95091472cecaa2541"}
|
||||
{"id":"evt-ytfrpb79ah3b","type":"edge.added","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-rjqy3hy","to":"area:security"},"at":"2026-09-19T13:32:34.300Z","parents":["evt-pd484j5j3hq5"],"hash":"b739aef21d7680d428ea744d2fecae2885844f1a7dc2f13c6aa17fc6e171a8f4"}
|
||||
{"id":"evt-49834dyrfxsz","type":"node.created","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Stop /api/discover returning raw error messages to the client","body":"src/app/api/discover/route.ts returns `err.message` verbatim in its 500 response, which breaks pri-tyrxdz9: upstream and OS errors can leak internal detail. Log the error server-side and return a generic message, as cameraErrorResponse does for the camera routes.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:36.415Z","parents":["evt-ytfrpb79ah3b"],"hash":"fa183e0add83870e060363268199dc28c43998bfed489828a439e4486297040e"}
|
||||
{"id":"evt-452arxb0d76h","type":"edge.added","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-dbwgww8","to":"gol-6q1q5mr"},"at":"2026-09-19T13:32:36.417Z","parents":["evt-49834dyrfxsz"],"hash":"c62021b0a468a7f9e32626f7ab1569057cc7dbeb274b05a4c9ed7bf1db409e9f"}
|
||||
{"id":"evt-8z642b5kxf22","type":"edge.added","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-dbwgww8","to":"src/app/api/discover/route.ts"},"at":"2026-09-19T13:32:36.418Z","parents":["evt-452arxb0d76h"],"hash":"e8a46cae5779e4ad0e819459b54e650b68bf99b5aabd62ad8f7a2891ad450208"}
|
||||
{"id":"evt-bza19sahqv8k","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-dbwgww8"},"at":"2026-09-19T13:32:36.419Z","parents":["evt-8z642b5kxf22"],"hash":"d8ee57c5c6add36a9a1f3472653b8b38c0ec4642c483fd3d02c19b2c8784503e"}
|
||||
{"id":"evt-031tzsx4b57t","type":"edge.added","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-dbwgww8","to":"area:api"},"at":"2026-09-19T13:32:36.420Z","parents":["evt-bza19sahqv8k"],"hash":"03d635f91a32d1e46b0dbcfbf68f2f877ed1e0115e82391baec08b49a36c18d6"}
|
||||
{"id":"evt-j5f7zhptkzzw","type":"edge.added","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-dbwgww8","to":"area:security"},"at":"2026-09-19T13:32:36.421Z","parents":["evt-031tzsx4b57t"],"hash":"9e7c2c17a96ce0bfc223cecdea037e25f8c58198540a0632ed2f2108fa5dec08"}
|
||||
{"id":"evt-z8cb3qepq17p","type":"node.created","subject":"iss-ksxmctm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Move camera info fetching in camera-card.tsx to a React Query hook","body":"Replace the useState, useEffect and `attempt` counter in CameraCard with useCameraInfo(id): useQuery with key ['camera', id, 'info'] calling /api/cameras/[id]/info. Map auth and inactive failures (problemFrom) to typed query errors. Retry becomes refetch(). The hook lives in a small client module of camera hooks with no device logic.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:41.746Z","parents":["evt-j5f7zhptkzzw"],"hash":"c5d03bb173afaf24302d907d1cc08cdfce0041da925d2b0cb96d358a0a05d172"}
|
||||
{"id":"evt-7pjd58ehewqd","type":"edge.added","subject":"iss-ksxmctm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-ksxmctm","to":"gol-146a3cb"},"at":"2026-09-19T13:32:41.748Z","parents":["evt-z8cb3qepq17p"],"hash":"77f9c673df77ee4b96ce4aa5d03a893f3fe43d2a71805b0ff7f23604822381bd"}
|
||||
{"id":"evt-1bn3taydyynf","type":"edge.added","subject":"iss-ksxmctm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-ksxmctm","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:32:41.749Z","parents":["evt-7pjd58ehewqd"],"hash":"de6f16af867d2abf6c5ec25be528c5497372688419d57c1c855ff086ff2fe846"}
|
||||
{"id":"evt-w5s3ytrr891y","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-a0hz0py","to":"iss-ksxmctm"},"at":"2026-09-19T13:32:41.750Z","parents":["evt-1bn3taydyynf"],"hash":"728be8299128949c7b165811d4e7bed487d869f486c324107915f3e578a8917c"}
|
||||
{"id":"evt-3ans5g5azk3n","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-ksxmctm"},"at":"2026-09-19T13:32:41.751Z","parents":["evt-w5s3ytrr891y"],"hash":"ad3cd0aa38c7c34442df04fceb99dc9a34a75a39e17b20996dff1d11e6b9edbb"}
|
||||
{"id":"evt-3vfengbvgv4s","type":"edge.added","subject":"iss-ksxmctm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-ksxmctm","to":"area:ui"},"at":"2026-09-19T13:32:41.752Z","parents":["evt-3ans5g5azk3n"],"hash":"2fedeb07fa40965c5ae7b817b5fa30634860da687aeed9d1f04e8124318ba63b"}
|
||||
{"id":"evt-f0egr0w1ghpp","type":"node.created","subject":"iss-m032zwq","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Move snapshot polling to React Query refetchInterval","body":"Replace the setTimeout poll loop in Snapshot with useQuery on key ['camera', id, 'snapshot'], using refetchInterval set to the chosen refresh rate and enabled only while info has loaded and there's no problem. Polling stops in hidden tabs by default. Keep revoking the previous object URL when a new frame arrives and on unmount. Acceptance: no memory growth from object URLs over a few minutes of polling, and an offline camera is not retried more than once per interval.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:44.362Z","parents":["evt-3vfengbvgv4s"],"hash":"e75ff82160e26d8ceb967b22f057f2b814559b4581a7210c2847fcd40a501c5f"}
|
||||
{"id":"evt-wzwx3h77wt1d","type":"edge.added","subject":"iss-m032zwq","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-m032zwq","to":"gol-146a3cb"},"at":"2026-09-19T13:32:44.363Z","parents":["evt-f0egr0w1ghpp"],"hash":"f2e391563a14c83c0f84344b291a38eec61c9699026816c56b858532a95cf030"}
|
||||
{"id":"evt-0z0hjypy0he2","type":"edge.added","subject":"iss-m032zwq","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-m032zwq","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:32:44.364Z","parents":["evt-wzwx3h77wt1d"],"hash":"e0d31ab72c76d4ad67bf24553b1325d7645b9b8163e15b217013132209cc9875"}
|
||||
{"id":"evt-yhnsfn4nrk9j","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-a0hz0py","to":"iss-m032zwq"},"at":"2026-09-19T13:32:44.365Z","parents":["evt-0z0hjypy0he2"],"hash":"d51b1ea8e3201c5530ccba16c89bee6f11d53149e3cdb3ad7e4e74992dd0fec9"}
|
||||
{"id":"evt-5ptc6qptgwz4","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-m032zwq"},"at":"2026-09-19T13:32:44.366Z","parents":["evt-yhnsfn4nrk9j"],"hash":"7dcd20a5779a10b131d1bb8e61d88d1d209ef16d6f24e8eefe1cd380b5926df3"}
|
||||
{"id":"evt-yg5j4gfmm9ft","type":"edge.added","subject":"iss-m032zwq","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-m032zwq","to":"area:ui"},"at":"2026-09-19T13:32:44.367Z","parents":["evt-5ptc6qptgwz4"],"hash":"99743510db7b977aaeff18ea5314a4af7cdc09fb3cc046a99a391957da613709"}
|
||||
{"id":"evt-yw9gex33ay6s","type":"node.created","subject":"iss-8hfq2y2","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Save and clear camera credentials with useMutation and invalidate that camera's queries","body":"The login form in camera-card.tsx currently calls fetch on /api/cameras/[id]/credentials (GET, PUT, DELETE) by hand. Use useQuery for the GET status and useMutation for PUT and DELETE. On success, invalidateQueries(['camera', id]) so info and snapshot refetch with the new login. Expected failures, such as a wrong password or an unreachable camera, are shown as form errors, not thrown.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:47.302Z","parents":["evt-yg5j4gfmm9ft"],"hash":"30d4ead146b4be11a7013042d8f0bc6ef4bc87567e2f66cc0733afa3f91489cf"}
|
||||
{"id":"evt-xhk82b9vcjyv","type":"edge.added","subject":"iss-8hfq2y2","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-8hfq2y2","to":"gol-146a3cb"},"at":"2026-09-19T13:32:47.304Z","parents":["evt-yw9gex33ay6s"],"hash":"e9521406aee626ea78bd26102ed4f622b69b93163954bf1ba0aa9eeab54b00e2"}
|
||||
{"id":"evt-ypx40vcx02p2","type":"edge.added","subject":"iss-8hfq2y2","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-8hfq2y2","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:32:47.305Z","parents":["evt-xhk82b9vcjyv"],"hash":"476497b321039f3484c7392d5fdf8060475d1ea911859a8676993598c4310ab8"}
|
||||
{"id":"evt-3ahcyz6tk1hm","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-a0hz0py","to":"iss-8hfq2y2"},"at":"2026-09-19T13:32:47.306Z","parents":["evt-ypx40vcx02p2"],"hash":"91e02eb11b06fb465f7398cb86eabae98951b6d9baf7b5ee02224f9dd22bbd19"}
|
||||
{"id":"evt-wj33w9feevzd","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-8hfq2y2"},"at":"2026-09-19T13:32:47.307Z","parents":["evt-3ahcyz6tk1hm"],"hash":"ad983a4baa411032461f5ad212166063f9f0ee413e7ec9c26117986993d724b8"}
|
||||
{"id":"evt-06rcrscag1zm","type":"edge.added","subject":"iss-8hfq2y2","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-8hfq2y2","to":"area:ui"},"at":"2026-09-19T13:32:47.308Z","parents":["evt-wj33w9feevzd"],"hash":"6e49a9b98136ba155a24aae418584b6343029825944ea04e58eae1116a9153b4"}
|
||||
{"id":"evt-z8hz7wfbmm83","type":"node.created","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Turn \"Scan network\" into a mutation that refreshes the server-rendered camera list","body":"Scan results currently live only in CameraScanner's client state. Make scanning a small client control that runs useMutation against POST /api/discover (which already writes the registry), then calls router.refresh() so the server-rendered list re-reads the registry. The camera list is no longer held in client state, and the scan options (timeout, unicast sweep) and the snapshot refresh-rate selector stay as small client controls.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:32:50.459Z","parents":["evt-06rcrscag1zm"],"hash":"df2afcf23dedfcdb378b77b905ffe886f8b7d62687ce2a47635e25ce50cf29e9"}
|
||||
{"id":"evt-jfa6d5nyk7sb","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-2fm6x2y","to":"gol-146a3cb"},"at":"2026-09-19T13:32:50.460Z","parents":["evt-z8hz7wfbmm83"],"hash":"047c5efc04151937a615d0b04ab0c185e2d9e6093b82ca9035ef65884535347e"}
|
||||
{"id":"evt-va8rts0h81x1","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-2fm6x2y","to":"src/app/camera-scanner.tsx"},"at":"2026-09-19T13:32:50.461Z","parents":["evt-jfa6d5nyk7sb"],"hash":"e2c7ddad03fde0a4da0092fc7804ac226da5fbfdffc33273ec90d5cb4f9952d2"}
|
||||
{"id":"evt-8a93gxsa25z7","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-2fm6x2y","to":"src/app/page.tsx"},"at":"2026-09-19T13:32:50.462Z","parents":["evt-va8rts0h81x1"],"hash":"ddd85037b2a90d97e95a948f5dfb691763aa32aab4f966739ff74bfd87bad1a2"}
|
||||
{"id":"evt-k59zqha559f5","type":"edge.added","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-a0hz0py","to":"iss-2fm6x2y"},"at":"2026-09-19T13:32:50.463Z","parents":["evt-8a93gxsa25z7"],"hash":"e6c7d1f6db7c099edbf123721428544042936841fb4c38d6ffdecb6fe3677871"}
|
||||
{"id":"evt-qcgz6258gtkq","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-2fm6x2y"},"at":"2026-09-19T13:32:50.464Z","parents":["evt-k59zqha559f5"],"hash":"1a87d28e7fe4a3c16ff56d78e60497498aaf1b408ad0b52c414ee7c7cb95cac7"}
|
||||
{"id":"evt-0srks6bg9fav","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-2fm6x2y","to":"area:ui"},"at":"2026-09-19T13:32:50.465Z","parents":["evt-qcgz6258gtkq"],"hash":"9110e2a468620653c4a0e61757192c20ade73e621db785e5c67b72f3fdd3775b"}
|
||||
{"id":"evt-et2mscnws7qe","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-2fm6x2y","to":"area:dashboard"},"at":"2026-09-19T13:32:50.466Z","parents":["evt-0srks6bg9fav"],"hash":"b1a17f2117ba6b00613ce1a492dbda4e85decd188c5490e87db79be38d62766f"}
|
||||
{"id":"evt-bm8mry4yvret","type":"node.created","subject":"gol-sjabnh3","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"goal","title":"Serve as an API server so third-party integrations can discover and use cameras","body":"Besides the web UI, the app exposes a documented, stable HTTP API that other systems (home automation, NVRs, scripts) can call to discover cameras on the network and use them: list known cameras, read info and status, fetch snapshots, and change settings. The dashboard and third parties go through the same server-side lib layer, so device logic is written once. Implies things the UI-only app can skip: a stable, versioned contract, authentication for non-browser callers, and error responses a machine can act on. Priority is low for now (weight 0.2), below the dashboard goals (1).","status":"active","owner":null,"attrs":{},"weight":0.2,"target":null,"direction":"up","unit":null},"at":"2026-09-19T13:36:39.284Z","parents":["evt-et2mscnws7qe"],"hash":"de5a9833e81ec4d55d23635be591ec929d64a401f3aa2d35fbdc2ba3eae7c6d5"}
|
||||
{"id":"evt-py79tb5z5cjx","type":"edge.added","subject":"gol-sjabnh3","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"gol-sjabnh3","to":"gol-6q1q5mr"},"at":"2026-09-19T13:36:39.285Z","parents":["evt-bm8mry4yvret"],"hash":"cd30b7d83d5a33737079ce7cc2f8e8ee62d209135f9381957fd90a8835dd8338"}
|
||||
{"id":"evt-9cwvv3a1h7wg","type":"edge.added","subject":"gol-sjabnh3","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-sjabnh3","to":"pri-m1csgrm"},"at":"2026-09-19T13:36:39.286Z","parents":["evt-py79tb5z5cjx"],"hash":"9c8b1e45dea5dbcbae33527617681d1a3fc221150a0d31dfbb9ed3c0ffde2f07"}
|
||||
{"id":"evt-4g852pmnbmj5","type":"edge.added","subject":"gol-sjabnh3","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-sjabnh3","to":"pri-tyrxdz9"},"at":"2026-09-19T13:36:39.287Z","parents":["evt-9cwvv3a1h7wg"],"hash":"54535842c40f2aa4ff9901f95ba8b480b439fa573ce0ce070494dc341bf019a2"}
|
||||
{"id":"evt-16g6xgs4xxzy","type":"edge.added","subject":"gol-sjabnh3","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-sjabnh3","to":"pri-p9h51hx"},"at":"2026-09-19T13:36:39.288Z","parents":["evt-4g852pmnbmj5"],"hash":"3e208d85a2d02e4cbac421ec2a67c482cf0f03be5539a9e7cad19e08f807e8cc"}
|
||||
{"id":"evt-pa0a7gftmx91","type":"node.updated","subject":"gol-6q1q5mr","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"weight":1}},"at":"2026-09-19T13:36:40.386Z","parents":["evt-16g6xgs4xxzy"],"hash":"7514e2d5b58786dff238503e0a1e352ad3cb393010287f82661c0f4fd6599482"}
|
||||
{"id":"evt-vqapqnjkfh90","type":"node.updated","subject":"gol-146a3cb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"weight":1}},"at":"2026-09-19T13:36:41.628Z","parents":["evt-pa0a7gftmx91"],"hash":"1764590f7adbec128eaaa2f7151c967250d9699337c5a4d27a0469c8e9f24012"}
|
||||
{"id":"evt-4axgmcdj2fh8","type":"node.created","subject":"ver-6xyjfyx","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"With a populated .data/cameras.json, GET / server-renders one tile per registry entry before any scan; page.tsx has no 'use client'; / is dynamic (not prerendered at build).","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:37:12.746Z","parents":["evt-vqapqnjkfh90"],"hash":"db199cd95b5d0b60f51c89842354da0295948de9a5ad168d7ed7f13517dc6fca"}
|
||||
{"id":"evt-p8szc580ewf0","type":"edge.added","subject":"ver-6xyjfyx","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-6xyjfyx","to":"iss-a0hz0py"},"at":"2026-09-19T13:37:12.748Z","parents":["evt-4axgmcdj2fh8"],"hash":"aa57e4e53ba530dae5376362f04b8af9284899e967e1df8ad7c8aa5d78f5694a"}
|
||||
{"id":"evt-nqd77pqc6ktb","type":"verification.recorded","subject":"ver-6xyjfyx","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pass","evidence":"2026-09-19: `npx next build` lists `ƒ /` (dynamic). `npx next start -p 3917`, then `curl -s localhost:3917/` rendered 2 tiles (192.168.17.129:80, 192.168.31.2:80), matching the 2 entries in .data/cameras.json. `?refresh=abc` falls back to 1 s (200). tsc --noEmit and eslint src are clean. Not checked in a browser: hydration and the snapshot polling UI weren't exercised."},"at":"2026-09-19T13:37:12.749Z","parents":["evt-p8szc580ewf0"],"hash":"84a1c6e46bf3d01af5fae5e7aac86bfd02afcc36c6370bede38ae0e02d934bfd"}
|
||||
{"id":"evt-4r4dsj0ghyc1","type":"node.created","subject":"ver-mgxv8kn","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"Invalid camera id, credentials body and discover body are rejected by zod with a 400 and a generic message, before any device or registry write.","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:37:15.201Z","parents":["evt-nqd77pqc6ktb"],"hash":"18e25e9feed819afe7e0c936b97cc8986486a767f506634ef1d49437de5422a7"}
|
||||
{"id":"evt-7n706y9fhcx3","type":"edge.added","subject":"ver-mgxv8kn","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-mgxv8kn","to":"iss-rjqy3hy"},"at":"2026-09-19T13:37:15.203Z","parents":["evt-4r4dsj0ghyc1"],"hash":"b4e2ccac63d4da17adfa5b2208e2669a73d597a47b38be974309053fafda0231"}
|
||||
{"id":"evt-y3j43c3y6ew5","type":"verification.recorded","subject":"ver-mgxv8kn","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pass","evidence":"2026-09-19 against `next start -p 3917`: GET /api/cameras/NOTANID/info returned 400 \"Invalid camera ID\"; an unknown valid UUID returned 404; PUT credentials with {\"username\":\"\"}, {\"username\":5} and non-JSON each returned 400 \"Username is required\"; POST /api/discover with {\"timeout\":\"x\"} and {\"timeout\":99999} each returned 400 \"Invalid scan options\". Schemas: cameraIdSchema (camera-registry.ts), credentialsSchema (credential-store.ts), discoverRequestSchema (onvif.ts), refreshMsSchema (app/refresh-rate.ts)."},"at":"2026-09-19T13:37:15.204Z","parents":["evt-7n706y9fhcx3"],"hash":"6976b7669f44e610435a7dae6a3479bceee3f25e61d50eb0254fbbcc34accc05"}
|
||||
{"id":"evt-p92q927w98nj","type":"node.created","subject":"ver-n9sa5s4","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"/api/discover never returns err.message; failures are logged server-side and the client gets a generic \"Network scan failed\".","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:37:17.620Z","parents":["evt-y3j43c3y6ew5"],"hash":"9dbc1d29a7cdcc1e08a6f3466179a73ac72e73c375fa682e7897d219ed7ff471"}
|
||||
{"id":"evt-ej5ww93txf38","type":"edge.added","subject":"ver-n9sa5s4","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-n9sa5s4","to":"iss-dbwgww8"},"at":"2026-09-19T13:37:17.621Z","parents":["evt-p92q927w98nj"],"hash":"6026f7b80c70e985e17a060cae5f3c9a57b5114278708a0c3071728c462d40e5"}
|
||||
{"id":"evt-9gh8610vnppz","type":"verification.recorded","subject":"ver-n9sa5s4","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pending","evidence":"Code change in src/app/api/discover/route.ts: the catch block now calls console.error and returns {error:\"Network scan failed\"} with a 500. Reviewed, but the 500 path was not exercised because I couldn't force a scan failure without breaking the host's network stack."},"at":"2026-09-19T13:37:17.622Z","parents":["evt-ej5ww93txf38"],"hash":"ee6a6c340af604313daf5de480ca59b8102d4712ff75b88b2dceef33fed09bb6"}
|
||||
{"id":"evt-7tg6sh0x2gzp","type":"node.status_changed","subject":"iss-a0hz0py","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"done"},"at":"2026-09-19T13:37:18.863Z","parents":["evt-9gh8610vnppz"],"hash":"9d7dd789eee12f2fe490feac05341ac4a68c8e4d2a9d3130667e0e82ed710777"}
|
||||
{"id":"evt-7czexnaf6c4h","type":"node.status_changed","subject":"iss-rjqy3hy","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"done"},"at":"2026-09-19T13:37:20.103Z","parents":["evt-7tg6sh0x2gzp"],"hash":"72b343bd059aba47d5e85d224272dadc74f4c98b656dc0477b92b89a64442e7d"}
|
||||
{"id":"evt-jrxhb8h0h4t5","type":"node.status_changed","subject":"iss-dbwgww8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"done"},"at":"2026-09-19T13:37:21.177Z","parents":["evt-7czexnaf6c4h"],"hash":"a454ae1749521ad7719d436b0b0800665f5be74e15243355e817ccde3eb65255"}
|
||||
{"id":"evt-jvt1zp0n2y3a","type":"node.created","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Stop cameraErrorResponse passing upstream camera error messages to the client","body":"iss-dbwgww8 described cameraErrorResponse (src/lib/camera-route.ts) as the generic-message model. It isn't: it returns err.message verbatim for every case, including the 502 fallback, so raw ONVIF/SOAP and socket errors reach the browser. That breaks pri-tyrxdz9. Keep the codes (inactive, auth, store) and write fixed user-facing messages for each; log the original server-side. Check that camera-card.tsx's auth 'reason' text still makes sense.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:37:29.051Z","parents":["evt-jrxhb8h0h4t5"],"hash":"3ed02e77abd89b30665a9568bb4cd876684d3f2a9cbef5ea5adf59c574c6842b"}
|
||||
{"id":"evt-dx3n4xph4z8a","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-tz5s098","to":"gol-6q1q5mr"},"at":"2026-09-19T13:37:29.052Z","parents":["evt-jvt1zp0n2y3a"],"hash":"a671815f9b2e041c844f23de37c049ce9175dafaa7afbf6fe0ceea6494de0779"}
|
||||
{"id":"evt-ddsqrs5kejjk","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-tz5s098","to":"gol-sjabnh3"},"at":"2026-09-19T13:37:29.053Z","parents":["evt-dx3n4xph4z8a"],"hash":"4d922fc99ea57f56eacac43a92b8f878be63962769419817cee50254c2486258"}
|
||||
{"id":"evt-7hwp833a6qzn","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-tz5s098","to":"src/lib/camera-route.ts"},"at":"2026-09-19T13:37:29.054Z","parents":["evt-ddsqrs5kejjk"],"hash":"d3c4710e44252760e5f592dcecb82de987f5dbf2647c7947c29d60aa15699548"}
|
||||
{"id":"evt-z23sf86wmkwp","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-tz5s098","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:37:29.055Z","parents":["evt-7hwp833a6qzn"],"hash":"fcb24029db67d49dbb24809de2d131595782bc0ded3b1869faf6d1940fa880ca"}
|
||||
{"id":"evt-fc3z9kkr5ss8","type":"edge.added","subject":"iss-qbh3541","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"parent_of","from":"iss-qbh3541","to":"iss-tz5s098"},"at":"2026-09-19T13:37:29.056Z","parents":["evt-z23sf86wmkwp"],"hash":"8083cf086de63c50174b7f4620907f9b3a78bdeacec989bea4cd8ae500d82ad8"}
|
||||
{"id":"evt-hdg4ccp9f02m","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-tz5s098","to":"area:api"},"at":"2026-09-19T13:37:29.057Z","parents":["evt-fc3z9kkr5ss8"],"hash":"6fbde4947b2de5d42b6c40665fa07cccfd93d5d89abca465e469881e9e223770"}
|
||||
{"id":"evt-mnerbabmd40b","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-tz5s098","to":"area:security"},"at":"2026-09-19T13:37:29.058Z","parents":["evt-hdg4ccp9f02m"],"hash":"cc19bbe88a3215d5cd7c69414c98a843cd5a5f047ace982adc41e41d69ccb309"}
|
||||
{"id":"evt-nt4sbgm3hsaj","type":"edge.added","subject":"iss-tz5s098","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"discovered_from","from":"iss-tz5s098","to":"iss-dbwgww8"},"at":"2026-09-19T13:37:29.059Z","parents":["evt-mnerbabmd40b"],"hash":"ba8f0d9b1b08e8278a0b6290de43d8e4a49a1b38d1a765708d44f3e281ac8b1a"}
|
||||
{"id":"evt-55fx8tkcd512","type":"node.created","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Fix build warning: dynamic filesystem paths make Turbopack trace the whole project","body":"`next build` warns \"Dynamic filesystem access causes tracing of the whole project\" at camera-registry.ts load() and credential-store.ts load(). Both build their paths from process.env and path.join(process.cwd(), '.data', ...). This existed before; it surfaced while building for iss-a0hz0py. Effect: all source files, including the public folder, get included in the server output. Fix per the warning's guidance, e.g. a static path or outputFileTracingExcludes; check the bundled docs first. Acceptance: next build shows no tracing warnings.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:37:31.978Z","parents":["evt-nt4sbgm3hsaj"],"hash":"bb43c0ff197a21ff431dbe4b147733cac73fe839059b6187fc68a436c51a5c63"}
|
||||
{"id":"evt-yvjync8ah2j4","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-jvxcd1n","to":"gol-6q1q5mr"},"at":"2026-09-19T13:37:31.979Z","parents":["evt-55fx8tkcd512"],"hash":"e24a93e08902e7ed2cbee7ca0e1e945c8b225deba7b4a7d4bc0e2f824cbeb098"}
|
||||
{"id":"evt-89tftvm9qcta","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-jvxcd1n","to":"src/lib/camera-registry.ts"},"at":"2026-09-19T13:37:31.980Z","parents":["evt-yvjync8ah2j4"],"hash":"fa413abe9fa1262adc26c7f5346cf4a923dfaaaf245114d3d9d60737b502a480"}
|
||||
{"id":"evt-6ra9093yhc00","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-jvxcd1n","to":"src/lib/credential-store.ts"},"at":"2026-09-19T13:37:31.981Z","parents":["evt-89tftvm9qcta"],"hash":"f0cee7c33b4834b8f0791799a875e44685e3217f4e79696508682678acab9d22"}
|
||||
{"id":"evt-xjtjda2yy9np","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-jvxcd1n","to":"next.config.ts"},"at":"2026-09-19T13:37:31.982Z","parents":["evt-6ra9093yhc00"],"hash":"32218b8c9894d2288f0ad46de2c4538bc92f561694fd86cfedd16c647e84436a"}
|
||||
{"id":"evt-6qtsfz3rv9rz","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-jvxcd1n","to":"area:build"},"at":"2026-09-19T13:37:31.983Z","parents":["evt-xjtjda2yy9np"],"hash":"d1054fb6b27a995c2f8f12f97455799e5c3bccb02e99c7cb28805ff902e453e5"}
|
||||
{"id":"evt-eev5ay33cf08","type":"edge.added","subject":"iss-jvxcd1n","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"discovered_from","from":"iss-jvxcd1n","to":"iss-a0hz0py"},"at":"2026-09-19T13:37:31.984Z","parents":["evt-6qtsfz3rv9rz"],"hash":"29c7239c6322b95973c7b414e81c947dd211fb9bf28033d9715fce3afafbaf60"}
|
||||
{"id":"evt-1snexdqy14kf","type":"node.updated","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"body":"Scan results used to live only in CameraScanner's client state. Partly done under iss-a0hz0py: the camera list is now server-rendered from the registry, the scanner calls router.refresh() after a successful scan, and the refresh-rate selector moved into the URL (?refresh=) as its own control (refresh-rate-select.tsx). Remaining: replace the scanner's hand-written fetch, useState and try/finally with useMutation against POST /api/discover, with onSuccess calling router.refresh()."}},"at":"2026-09-19T13:37:34.073Z","parents":["evt-eev5ay33cf08"],"hash":"f6c615fd26a426793a56b4595dbb8c27785477a3595417ca475a66510ad8c68f"}
|
||||
{"id":"evt-8kaa72vcy7bt","type":"node.created","subject":"ver-zfcakm1","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"The change is committed and can be re-checked from the repo.","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:40:36.667Z","parents":["evt-1snexdqy14kf"],"hash":"bc5f648ce98a710bc2fb73d569f609532cb23a1d2b425c35004d1cee2ebddf96"}
|
||||
{"id":"evt-fznxevbm4afh","type":"edge.added","subject":"ver-zfcakm1","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-zfcakm1","to":"iss-a0hz0py"},"at":"2026-09-19T13:40:36.668Z","parents":["evt-8kaa72vcy7bt"],"hash":"2fed91eee6c65fe1a656819863ae91f47f85677447ee6635a11431bf587dd95c"}
|
||||
{"id":"evt-px9ckhw092qh","type":"verification.recorded","subject":"ver-zfcakm1","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pass","evidence":"Commit f39d16a on branch camera-dashboard. Re-check: `git checkout f39d16a && npx next build`; the route table should show `ƒ /`, then `npx next start` and GET / should list the registry cameras."},"at":"2026-09-19T13:40:36.669Z","parents":["evt-fznxevbm4afh"],"hash":"686d03f4eb2fefcd1edbdf2b0a83c6b3ce01045e50ab777dc4ccba969bd97ec6"}
|
||||
{"id":"evt-35fqwhnzvtkn","type":"node.created","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"goal","title":"95% automated test coverage","body":"Line coverage of the app's source (src/**), measured by the test runner's coverage report on every run, is at least 95%. The coverage command and exact file scope get fixed once the test tooling is chosen (see the tooling question). Why: this app holds admin logins for every camera on the network and is headed toward a third-party API (gol-sjabnh3), so security properties need automated regression checks instead of one-off manual curl runs. Examples: no secrets or upstream errors in responses (iss-dbwgww8, iss-tz5s098), input validation, and private-IP-only targets. Device-facing code (ONVIF discovery, SOAP, snapshots) is tested against local fake devices built on Node's http and dgram, never real cameras. Baseline 2026-09-19: 0%, since there are no tests.","status":"active","owner":null,"attrs":{},"weight":null,"target":95,"direction":"up","unit":"%"},"at":"2026-09-19T13:44:39.758Z","parents":["evt-px9ckhw092qh"],"hash":"9933926b8e0271cc9347a2536950b103c8c1bd1cd3c22d26cc9c77b3a4d17f8f"}
|
||||
{"id":"evt-xybetm8tvpjn","type":"edge.added","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"gol-9zxah3p","to":"gol-6q1q5mr"},"at":"2026-09-19T13:44:39.759Z","parents":["evt-35fqwhnzvtkn"],"hash":"9f61d961c88fbc7820e295c9c9c3fab115afd502258150270af79c85a0c426df"}
|
||||
{"id":"evt-m3z241wyf7qq","type":"edge.added","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"gol-9zxah3p","to":"gol-sjabnh3"},"at":"2026-09-19T13:44:39.760Z","parents":["evt-xybetm8tvpjn"],"hash":"57add2064648b76af7bf8747d6459d39e7921a98638354b1b9fc34052bec7bda"}
|
||||
{"id":"evt-s65d56015qpr","type":"edge.added","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-9zxah3p","to":"pri-tyrxdz9"},"at":"2026-09-19T13:44:39.761Z","parents":["evt-m3z241wyf7qq"],"hash":"5c9af0a7dbbd92350285a1e531e99ff4a7f279c018eb098057f502fe5b1c6dea"}
|
||||
{"id":"evt-7kr43tjk3bd0","type":"edge.added","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"upholds","from":"gol-9zxah3p","to":"pri-m1csgrm"},"at":"2026-09-19T13:44:39.762Z","parents":["evt-s65d56015qpr"],"hash":"986006ae0e59e8d75edca980b3939a6bbd6a5104e477829cae1eb2bc4d41af32"}
|
||||
{"id":"evt-gvf2zr8741c7","type":"node.created","subject":"que-gsp8rhh","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"question","title":"Which test tooling should we use to reach 95% coverage: Node's built-in test runner, or Vitest + Testing Library?","body":"Dependency justification per pri-mz2jxpb. Node is v24.14.1, which runs .ts directly (type stripping) and has node:test with built-in coverage (--experimental-test-coverage, --test-coverage-lines=95). That needs zero new packages, but it can't run .tsx: no JSX transform, and no DOM for client components. The Next 16 bundled guide (02-guides/testing/vitest.md) recommends: npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom vite-tsconfig-paths, plus @vitest/coverage-v8 for coverage. That's about 7 dev dependencies with a large transitive tree (vite, a bundler, jsdom). It runs .ts and .tsx with one runner and one coverage report. Either way, the Next docs say async Server Components (our page.tsx) are unsupported by unit runners and recommend E2E (Playwright, which downloads browsers) for those. Everything here is dev-only; nothing ships to the client. Removal is easy either way, since tests are isolated.","status":"open","owner":null,"attrs":{"options":[{"option":"node:test only (zero dependencies)","consequence":"src/lib and route handlers get tested, which is where the security logic lives. Client .tsx components and page.tsx can't be run, so either the 95% scope excludes them or they need component logic pulled out into plain .ts."},{"option":"Vitest + Testing Library + jsdom + coverage-v8 (~7 dev deps)","consequence":"One runner covers lib, routes and client components; 95% over all of src except the async page.tsx."},{"option":"Either of the above plus Playwright for E2E","consequence":"Also covers page.tsx and full flows against next start, at the cost of a browser download and slower runs."}],"revisit":null}},"at":"2026-09-19T13:44:46.870Z","parents":["evt-7kr43tjk3bd0"],"hash":"fe297caac30b10317d8b0498d3d533dd48cbb60a05384a0c1c011cf53f06c18a"}
|
||||
{"id":"evt-18tex4vv69sz","type":"edge.added","subject":"que-gsp8rhh","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"que-gsp8rhh","to":"gol-6q1q5mr"},"at":"2026-09-19T13:44:46.871Z","parents":["evt-gvf2zr8741c7"],"hash":"22cb6705f0138a595881bf0fc55de1e7c75cb58dcd2a46cafc17c4ba193743f3"}
|
||||
{"id":"evt-b5wtcn58qr6s","type":"edge.added","subject":"pri-mz2jxpb","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"guides","from":"pri-mz2jxpb","to":"que-gsp8rhh"},"at":"2026-09-19T13:44:46.872Z","parents":["evt-18tex4vv69sz"],"hash":"570b426c32267ebb36110aa5df7c18b833c5f0002565d3f5324d647c0dad3640"}
|
||||
{"id":"evt-jde40zstj5a9","type":"node.created","subject":"dec-f62003t","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"decision","title":"Use Vitest + Testing Library (jsdom, coverage-v8) as the test tooling","body":"Chosen by the user on 2026-09-19. Dev dependencies: vitest, @vitejs/plugin-react, jsdom, @testing-library/react, @testing-library/dom, vite-tsconfig-paths, @vitest/coverage-v8. Rejected: node:test only (can't run .tsx client components) and adding Playwright E2E now (browser download, slower; can be revisited for page.tsx). Coverage is measured with v8 over src/**, excluding the async page.tsx, which Next's docs say unit runners can't render.","status":"recorded","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:45:09.379Z","parents":["evt-b5wtcn58qr6s"],"hash":"773176803cf90466556fa4580782e0fed6631093757ca9e48af49b93b56ac2ed"}
|
||||
{"id":"evt-2d1ty0mpd6gg","type":"edge.added","subject":"dec-f62003t","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"answers","from":"dec-f62003t","to":"que-gsp8rhh"},"at":"2026-09-19T13:45:09.380Z","parents":["evt-jde40zstj5a9"],"hash":"a94aab756339eeb72ab45456afa8ea26d1ab5fd55e2b8073f02356dc09feb5e2"}
|
||||
{"id":"evt-w51qwmfgbdnj","type":"question.answered","subject":"que-gsp8rhh","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"answer":"dec-f62003t"},"at":"2026-09-19T13:45:09.381Z","parents":["evt-2d1ty0mpd6gg"],"hash":"76ee63bc78183bc102fcc9606962e0ac82860b4ce65223e61cc626b41a184c6f"}
|
||||
{"id":"evt-gzczh9apfdqp","type":"edge.added","subject":"dec-f62003t","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"dec-f62003t","to":"gol-9zxah3p"},"at":"2026-09-19T13:45:09.382Z","parents":["evt-w51qwmfgbdnj"],"hash":"50ce48ffce0ad5deab75ba8ffa83a3e60f9e2f5698bf0cb2bb993f7355e4e3ce"}
|
||||
{"id":"evt-97k3q00prk28","type":"node.updated","subject":"gol-9zxah3p","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"patch":{"weight":1}},"at":"2026-09-19T13:45:10.389Z","parents":["evt-gzczh9apfdqp"],"hash":"09d84be013a1b1886f0dc4de592de83910423c195aad0d25485b535d1a20cdf0"}
|
||||
{"id":"evt-vtx2d6pd5cm3","type":"node.created","subject":"mea-gwsw0mg","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"measurement","title":"Line coverage of src/** (excluding page.tsx, *.d.ts, tests)","body":"`npm run coverage` (vitest run --coverage, v8 provider, scope from vitest.config.mts) on branch camera-dashboard, 2026-09-19: 140/449 lines, 68 tests. Per file: camera-registry.ts 96.8%, credential-store.ts 100%, api/discover/route.ts 100%, refresh-rate.ts 100%, camera.ts 13.5%, onvif.ts 7.3%; the other routes and all client components 0%.","status":"recorded","owner":null,"attrs":{"value":31.18,"applies_at":"2026-09-19T13:48:06.459Z"}},"at":"2026-09-19T13:48:06.459Z","parents":["evt-97k3q00prk28"],"hash":"befdf8422be0bb96af307380b4a907bbec1fcd94b1be66f3e9c3f987a830fac2"}
|
||||
{"id":"evt-afxnff1fbntk","type":"edge.added","subject":"mea-gwsw0mg","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"measures","from":"mea-gwsw0mg","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:06.461Z","parents":["evt-vtx2d6pd5cm3"],"hash":"a6c2cd2d488d9d0a539af6f4840990072c0d1db577f501bb86f80c00f131dc73"}
|
||||
{"id":"evt-88gwxwnpfb9q","type":"node.created","subject":"ver-wx8n3pt","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"When discovery throws, recording results throws, or a non-Error is thrown, POST /api/discover returns 500 {\"error\":\"Network scan failed\"}, the body contains none of the original message, and the error is logged server-side.","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:48:08.940Z","parents":["evt-afxnff1fbntk"],"hash":"23468a62705a056e210d76ad210a849ca2fd2fc370daf94ba2a462a5a58df5ae"}
|
||||
{"id":"evt-0feac5f7j5k3","type":"edge.added","subject":"ver-wx8n3pt","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-wx8n3pt","to":"iss-dbwgww8"},"at":"2026-09-19T13:48:08.942Z","parents":["evt-88gwxwnpfb9q"],"hash":"3d4ef51bdba198c66f89bbe3a80e938beccd561f0207112dd8d70493eb977b8e"}
|
||||
{"id":"evt-negjfqhsg0tt","type":"verification.recorded","subject":"ver-wx8n3pt","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pass","evidence":"src/app/api/discover/route.test.ts, 'when the scan fails' (3 cases) with a thrown message containing a fake EISDIR path. Run `npx vitest run src/app/api/discover`: 11/11 pass on 2026-09-19."},"at":"2026-09-19T13:48:08.943Z","parents":["evt-0feac5f7j5k3"],"hash":"baf7834a9152cb1a7a91fb5244be0a13262321a0143e43bf921b9586b45d03b4"}
|
||||
{"id":"evt-4qn792azvgn3","type":"node.created","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Set up Vitest with coverage reporting","body":"vitest.config.mts: react plugin, native tsconfigPaths, a server-only stub (test/server-only.ts), node environment by default with jsdom opt-in per file, and v8 coverage over src/** excluding page.tsx, *.d.ts and tests. Scripts: test, test:watch, coverage. test/temp-data.ts gives each test an isolated registry and credential store. vite-tsconfig-paths from the Next guide was dropped because Vite 8 resolves tsconfig paths natively. @types/node was bumped from ^20 to ^24 to match the Node 24 runtime (vitest 5 peer requirement).","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:11.642Z","parents":["evt-negjfqhsg0tt"],"hash":"ba67b1f95d4531bc6935477fc561e3edf424cf75781128ebdc70f40ed4d7484b"}
|
||||
{"id":"evt-jvkf01egsdzc","type":"edge.added","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-fp7qh13","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:11.643Z","parents":["evt-4qn792azvgn3"],"hash":"290e1d4e3d18be01a592f825cc50fc614dd654739a92687d780b98f076e5cb2c"}
|
||||
{"id":"evt-aygkt9sma1g1","type":"edge.added","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-fp7qh13","to":"vitest.config.mts"},"at":"2026-09-19T13:48:11.644Z","parents":["evt-jvkf01egsdzc"],"hash":"6ed844b0efb14169a848978ffca2f16130d2d68fce5582966d7ec2e81bd25f6d"}
|
||||
{"id":"evt-ec6jdy5gcakv","type":"edge.added","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-fp7qh13","to":"package.json"},"at":"2026-09-19T13:48:11.645Z","parents":["evt-aygkt9sma1g1"],"hash":"7b827c9dc3452e819ea7c98cf3b0d706a924fbb1fea0b63509a933e689bc7a22"}
|
||||
{"id":"evt-fd8fq7v39gg9","type":"edge.added","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-fp7qh13","to":"test/"},"at":"2026-09-19T13:48:11.646Z","parents":["evt-ec6jdy5gcakv"],"hash":"ef4bd565012d1c0b386dd748fc2f9dfae2d57dfa68ea2d5918cec0b0d69f884b"}
|
||||
{"id":"evt-gg0g109sjkkf","type":"edge.added","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-fp7qh13","to":"area:testing"},"at":"2026-09-19T13:48:11.647Z","parents":["evt-fd8fq7v39gg9"],"hash":"85f7d95708c6369b16102690d65b4b1d71bb9be8841d335473a25fd105eeb721"}
|
||||
{"id":"evt-fskrsqn0c5db","type":"node.created","subject":"ver-5nx699s","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"verification","title":"`npm test` runs the suite green, `npm run coverage` prints a v8 report for src/**, and tsc and eslint are clean with the test files included.","body":"","status":"pending","owner":"prn-q80g8mz","attrs":{}},"at":"2026-09-19T13:48:16.296Z","parents":["evt-gg0g109sjkkf"],"hash":"0efccba5342755be86de3bda42aafd7186c6d640d137bfab43ecb32b7784b803"}
|
||||
{"id":"evt-mkrpremw61he","type":"edge.added","subject":"ver-5nx699s","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"evidence_for","from":"ver-5nx699s","to":"iss-fp7qh13"},"at":"2026-09-19T13:48:16.298Z","parents":["evt-fskrsqn0c5db"],"hash":"6f5a81eb28d6911ec28cb762bce6dc8b38e8288c2d78053ce9abc3e48bd934f9"}
|
||||
{"id":"evt-1jn7ct7mv04m","type":"verification.recorded","subject":"ver-5nx699s","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"result":"pass","evidence":"2026-09-19: npx vitest run gave 5 files and 68 tests passing; coverage report 31.18% lines; npx tsc --noEmit and npx eslint src test vitest.config.mts clean."},"at":"2026-09-19T13:48:16.299Z","parents":["evt-mkrpremw61he"],"hash":"5f2c70dcef7be27a9929c596f45fbeeaf6953b2ae6104a201cc43240d5c7936b"}
|
||||
{"id":"evt-jkrbz6k6tzc5","type":"node.status_changed","subject":"iss-fp7qh13","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"from":"open","to":"done"},"at":"2026-09-19T13:48:17.363Z","parents":["evt-1jn7ct7mv04m"],"hash":"10c06b7a73b9d5a6510d36beb60dbab835cf266d51e94dd5406d612e1809c314"}
|
||||
{"id":"evt-bwfkby1sn3kz","type":"node.created","subject":"iss-qak2mz8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Test camera.ts against a fake ONVIF/HTTP device","body":"camera.ts is at 13.5% line coverage. Stand up a local node:http server on 127.0.0.1 that answers the ONVIF SOAP calls (GetDeviceInformation, GetProfiles, GetSnapshotUri) and serves a snapshot with digest auth. Tests must bypass isAllowedHost, since the fake runs on loopback, by calling lib functions with a CameraTarget directly. Cover: info mapping, snapshot content-type and body, digest auth, auth-failure → CameraAuthError, inactive device → CameraInactiveError, timeouts, connection reuse and resetConnection. Also cover isAllowedHost edge cases: 172.15/172.32, octets above 255, IPv6.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:20.308Z","parents":["evt-jkrbz6k6tzc5"],"hash":"ab889741116691b0fe059dfc03e672ebed458367ee428d5b2110856e5029c852"}
|
||||
{"id":"evt-ps83bhknrgcz","type":"edge.added","subject":"iss-qak2mz8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-qak2mz8","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:20.309Z","parents":["evt-bwfkby1sn3kz"],"hash":"842b3b8363813141238744ef69db71eabe09851b22f75b76ac86c9aa63c1af30"}
|
||||
{"id":"evt-x0feparnhmmz","type":"edge.added","subject":"iss-qak2mz8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-qak2mz8","to":"src/lib/camera.ts"},"at":"2026-09-19T13:48:20.310Z","parents":["evt-ps83bhknrgcz"],"hash":"ed7f9db7b26badb71a59548b86535f41a4b62ab636dfc8b5e2bec491e385f9a3"}
|
||||
{"id":"evt-xtdmjnt8ddm1","type":"edge.added","subject":"iss-qak2mz8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-qak2mz8","to":"area:testing"},"at":"2026-09-19T13:48:20.311Z","parents":["evt-x0feparnhmmz"],"hash":"8a9ac6b2ffc60b629e8fd6e45e6c3ad2a5055265d558c62ab21a0e18ad26ac3a"}
|
||||
{"id":"evt-ws9ef8fkm6qj","type":"node.created","subject":"iss-3bg4r6e","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Test onvif.ts discovery against a fake WS-Discovery responder","body":"onvif.ts is at 7.3% line coverage. Use node:dgram to answer probes with canned ProbeMatch SOAP. Cover response parsing (XAddrs, scopes → name, hardware and location, endpoint reference → id), de-duplicating the same camera found by multicast and unicast (foundBy), the subnet-size limit for the unicast sweep (/20), timeouts, and malformed responses. Where real multicast is flaky in CI, inject the socket or interfaces instead of sending real multicast; open a design issue if that needs a code seam.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:23.727Z","parents":["evt-xtdmjnt8ddm1"],"hash":"1b1b0e2cc289eddfd224f95ff753048db66f2e3028758eae4c6f99848446acc5"}
|
||||
{"id":"evt-68eqe8bgyy7p","type":"edge.added","subject":"iss-3bg4r6e","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-3bg4r6e","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:23.728Z","parents":["evt-ws9ef8fkm6qj"],"hash":"50331a3cc0bc6fcd4c8207e0b262377a503e5373f47179100d3dd8affac9f5b4"}
|
||||
{"id":"evt-mh4x418v3x2q","type":"edge.added","subject":"iss-3bg4r6e","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-3bg4r6e","to":"src/lib/onvif.ts"},"at":"2026-09-19T13:48:23.729Z","parents":["evt-68eqe8bgyy7p"],"hash":"7f9375c8ab6cb0efa2264d27b02df989060d05c2d88e5422a1e7b2a05c128df7"}
|
||||
{"id":"evt-wem03cgmd3af","type":"edge.added","subject":"iss-3bg4r6e","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-3bg4r6e","to":"area:testing"},"at":"2026-09-19T13:48:23.730Z","parents":["evt-mh4x418v3x2q"],"hash":"9c56f42d9ac19c5ce940e2d49a6bc4c2103551a093c4e6eae40dcc96c88962fe"}
|
||||
{"id":"evt-8f99b8nrsd0a","type":"node.created","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Test the camera info, snapshot and credentials route handlers","body":"All three are at 0% line coverage. Mock @/lib/camera, and the credential store where needed. Cover: bad id → 400, unknown → 404; info passthrough; snapshot bytes, Content-Type, Cache-Control no-store and the ?profile param; credentials GET never includes a password; PUT validates with zod, stores only after testCredentials succeeds, and calls resetConnection; DELETE clears; each error class maps via cameraErrorResponse. Write the error-leak assertions so they encode iss-tz5s098's fixed behaviour.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:27.546Z","parents":["evt-wem03cgmd3af"],"hash":"faff1c604c1bf647dd45e1f27dca1b965a6e0e89dc291f0b1fcc97fe80a0c421"}
|
||||
{"id":"evt-zy1r77zzm925","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-nc2tj7c","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:27.547Z","parents":["evt-8f99b8nrsd0a"],"hash":"10f96109b30d634cec5c143d980768d97ce447ce3af7b196547bc978b08e65f9"}
|
||||
{"id":"evt-ej6sj2fzfwd6","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc2tj7c","to":"src/app/api/cameras/[id]/info/route.ts"},"at":"2026-09-19T13:48:27.548Z","parents":["evt-zy1r77zzm925"],"hash":"9bb3032a339b45dc1b3827d0206796026123678f2affb264375e323cc38c4847"}
|
||||
{"id":"evt-p34m0fbkpe11","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc2tj7c","to":"src/app/api/cameras/[id]/snapshot/route.ts"},"at":"2026-09-19T13:48:27.549Z","parents":["evt-ej6sj2fzfwd6"],"hash":"2c0a4ce242138cf030f9e88579fdf35d82dc99111f5b870b926849d11dcc0a20"}
|
||||
{"id":"evt-d8ft09wp5tmk","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc2tj7c","to":"src/app/api/cameras/[id]/credentials/route.ts"},"at":"2026-09-19T13:48:27.550Z","parents":["evt-p34m0fbkpe11"],"hash":"30f88782f74bdb5d99260b116923f9ea52d7281d4b2a70fa94e327d1cf67f3d0"}
|
||||
{"id":"evt-vb0vkr6qda21","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-nc2tj7c","to":"area:testing"},"at":"2026-09-19T13:48:27.551Z","parents":["evt-d8ft09wp5tmk"],"hash":"7a0b378b31697ce002c74f733c1344fafebca29f122f3baa33ff6c75577db27e"}
|
||||
{"id":"evt-j6f03bgr07aw","type":"edge.added","subject":"iss-nc2tj7c","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-nc2tj7c","to":"area:api"},"at":"2026-09-19T13:48:27.552Z","parents":["evt-vb0vkr6qda21"],"hash":"5b3fb45ed96e8b28ddc08e1bab2d2b9e45e0ff0d6dacb4a54b1ef29d5ed14e32"}
|
||||
{"id":"evt-n4bsjrhgbzb2","type":"node.created","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Component tests for camera-card, camera-scanner, refresh-rate-select, providers and layout","body":"All client components are at 0% line coverage. Use jsdom with Testing Library, and stub fetch with vi.stubGlobal plus next/navigation's useRouter and usePathname. Best done after the React Query migration issues (iss-ksxmctm, iss-m032zwq, iss-8hfq2y2, iss-2fm6x2y), so the tests are written against the new hooks rather than code about to be replaced. Cover the setup, login and error panels, snapshot pause and resume, the scan flow calling router.refresh, and the refresh selector writing ?refresh=. layout.tsx needs next/font/google mocked.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:32.191Z","parents":["evt-j6f03bgr07aw"],"hash":"7b0ef50b53b3ea4bf0cbc88cbeb1ced59fe7f82ec0fbd4f7b5520e700999fd45"}
|
||||
{"id":"evt-yv8ep34jkagb","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-nc5w0j8","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:32.192Z","parents":["evt-n4bsjrhgbzb2"],"hash":"6eedbbb04ef7464317c25a9ef206cf477e469d874c70e9d924b8600ebe55fade"}
|
||||
{"id":"evt-mpnncz77ahfk","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-nc5w0j8","to":"gol-146a3cb"},"at":"2026-09-19T13:48:32.193Z","parents":["evt-yv8ep34jkagb"],"hash":"921bd2e707196cfa5a0bdcd3a622c7df8e5d9eb73826e1a9b9a3b2f842ebdab5"}
|
||||
{"id":"evt-jmgs8twwfpa1","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc5w0j8","to":"src/app/camera-card.tsx"},"at":"2026-09-19T13:48:32.194Z","parents":["evt-mpnncz77ahfk"],"hash":"53f9469ee293c53bfd281ccc06047c1100619c2f05a71ee6e2b0586d3881d034"}
|
||||
{"id":"evt-w208y81sz70m","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc5w0j8","to":"src/app/camera-scanner.tsx"},"at":"2026-09-19T13:48:32.195Z","parents":["evt-jmgs8twwfpa1"],"hash":"7692d17365f8a74c1d1809f60ad6c1e1bb0bc609062ef01700925c0f370b7017"}
|
||||
{"id":"evt-dpw94b847yqh","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc5w0j8","to":"src/app/refresh-rate-select.tsx"},"at":"2026-09-19T13:48:32.196Z","parents":["evt-w208y81sz70m"],"hash":"ef61e6216cd5fb4bcc8fa04b237aab8ac6575bae62b172c2fdcf2f698c147b02"}
|
||||
{"id":"evt-d9a1pyzsavm0","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc5w0j8","to":"src/app/providers.tsx"},"at":"2026-09-19T13:48:32.197Z","parents":["evt-dpw94b847yqh"],"hash":"318f40626918950a0f7e0a2ce4fe31f04eaef70962d9547bc9885a2db2dc5df9"}
|
||||
{"id":"evt-4qxyc2kyq2cr","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-nc5w0j8","to":"src/app/layout.tsx"},"at":"2026-09-19T13:48:32.198Z","parents":["evt-d9a1pyzsavm0"],"hash":"ab5a8f9cb35167d3df6374d3fa4c60d90f01e0bfb8370cae5bd13feb3217d6b7"}
|
||||
{"id":"evt-f5f5c36cmdwx","type":"edge.added","subject":"iss-ksxmctm","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-ksxmctm","to":"iss-nc5w0j8"},"at":"2026-09-19T13:48:32.199Z","parents":["evt-4qxyc2kyq2cr"],"hash":"6e288cdbcb20e5500ef8a8a09207a28068f8ae65868017f73da6ac5aa02955a7"}
|
||||
{"id":"evt-p9e0jd6d0q3n","type":"edge.added","subject":"iss-m032zwq","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-m032zwq","to":"iss-nc5w0j8"},"at":"2026-09-19T13:48:32.200Z","parents":["evt-f5f5c36cmdwx"],"hash":"f03d5eafa729c5f06843c18798c58b82d1e59f57d95458f6766a20c846c751a5"}
|
||||
{"id":"evt-te4s5w0j0ngr","type":"edge.added","subject":"iss-8hfq2y2","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-8hfq2y2","to":"iss-nc5w0j8"},"at":"2026-09-19T13:48:32.201Z","parents":["evt-p9e0jd6d0q3n"],"hash":"4fa9a7286a62d04a9ce1babb721e5c514167f61997202f867d355f654b7ec41e"}
|
||||
{"id":"evt-enyfg19tyw7b","type":"edge.added","subject":"iss-2fm6x2y","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"blocks","from":"iss-2fm6x2y","to":"iss-nc5w0j8"},"at":"2026-09-19T13:48:32.202Z","parents":["evt-te4s5w0j0ngr"],"hash":"5d9a430cbf16a56be8597ed53a6dd2224668525f633c90a1aafeb0db51854f69"}
|
||||
{"id":"evt-1b7znc85203x","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-nc5w0j8","to":"area:testing"},"at":"2026-09-19T13:48:32.203Z","parents":["evt-enyfg19tyw7b"],"hash":"61627b32dbda33ae92ec7f27d354f386d2e1ef31b87001a19f5519cbb22deb13"}
|
||||
{"id":"evt-p2kpk0wtdsgv","type":"edge.added","subject":"iss-nc5w0j8","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-nc5w0j8","to":"area:ui"},"at":"2026-09-19T13:48:32.204Z","parents":["evt-1b7znc85203x"],"hash":"5dce2df80199d0e3f98bf31002604aae457629297b61bdb468acf1364fe82da9"}
|
||||
{"id":"evt-b5118ev8dnhf","type":"node.created","subject":"iss-zjpc22k","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"issue","title":"Enforce the 95% line-coverage threshold in vitest.config.mts","body":"Once coverage reaches 95%, set coverage.thresholds.lines = 95 so `npm run coverage` fails if it drops. Until then, measurements are recorded on gol-9zxah3p after each batch of tests.","status":"open","owner":null,"attrs":{}},"at":"2026-09-19T13:48:34.119Z","parents":["evt-p2kpk0wtdsgv"],"hash":"db62b67eb50e908053f5b4b4a96aaf0d571a592eda56ffe85f468ad61d6c5822"}
|
||||
{"id":"evt-zb67vbj9gq3f","type":"edge.added","subject":"iss-zjpc22k","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"serves","from":"iss-zjpc22k","to":"gol-9zxah3p"},"at":"2026-09-19T13:48:34.121Z","parents":["evt-b5118ev8dnhf"],"hash":"47a2a3f02b139eab3f36583eb856278791a46e9e6035cad834de35f8aeca3f49"}
|
||||
{"id":"evt-9jqc4c9akpvd","type":"edge.added","subject":"iss-zjpc22k","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"touches","from":"iss-zjpc22k","to":"vitest.config.mts"},"at":"2026-09-19T13:48:34.122Z","parents":["evt-zb67vbj9gq3f"],"hash":"2dce8ab9c28c069b400e6bfbcbb1859ea66e49e72c4c36dab1e7c1386c2cd67e"}
|
||||
{"id":"evt-tgnhg3bzctxe","type":"edge.added","subject":"iss-zjpc22k","actor":"prn-q80g8mz","actor_kind":"agent","session":null,"payload":{"kind":"tagged","from":"iss-zjpc22k","to":"area:testing"},"at":"2026-09-19T13:48:34.123Z","parents":["evt-9jqc4c9akpvd"],"hash":"f5502f9006c88d881121d0b5ea5750d9a179ac754b1d53aab5469bb2e5d9ebaf"}
|
||||
Loading…
Reference in New Issue
Block a user