@dailephd/my-frontend-observer 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +490 -479
- package/LICENSE +21 -21
- package/README.md +375 -365
- package/dist/application/projectCheckService.d.ts +6 -0
- package/dist/application/projectCheckService.js +8 -1
- package/dist/application/projectCheckService.js.map +1 -1
- package/dist/application/projectWorkflowService.d.ts +7 -2
- package/dist/application/projectWorkflowService.js +10 -3
- package/dist/application/projectWorkflowService.js.map +1 -1
- package/dist/cli.js +510 -510
- package/dist/viewer/index.html +13 -13
- package/dist/viewer/sw.js +1 -1
- package/docs/ARCHITECTURE.md +1394 -1385
- package/docs/CI_CD.md +349 -338
- package/docs/COMMANDS.md +1035 -1026
- package/docs/CONTRACTS.md +1971 -1960
- package/docs/CURRENT_STATE.md +1277 -1252
- package/docs/DEVELOPMENT.md +240 -237
- package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -50
- package/docs/PROJECT_DESCRIPTION.md +2248 -2224
- package/docs/PROJECT_MILESTONES.md +2681 -2558
- package/docs/PROJECT_OVERVIEW.md +200 -196
- package/docs/QUICKSTART.md +100 -100
- package/docs/RELEASE.md +37 -36
- package/docs/ROADMAP.md +1105 -1034
- package/docs/SECURITY.md +297 -297
- package/docs/WORKFLOWS.md +806 -796
- package/docs/plans/v0.10-implementation-plan.md +1509 -1509
- package/docs/plans/v0.8-implementation-plan.md +655 -655
- package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -505
- package/docs/plans/v0.9-implementation-plan.md +1529 -1529
- package/docs/plans/v0.9.1-implementation-plan.md +468 -468
- package/docs/reports/v0.10-batch1-visual-change-workflow-foundation.md +102 -102
- package/docs/reports/v0.10-batch2-project-composition-check-recording.md +103 -103
- package/docs/reports/v0.10-batch3-viewer-visual-change-workspace.md +93 -93
- package/docs/reports/v0.10-batch4-actual-frontend-entry.md +59 -59
- package/docs/reports/v0.10-batch5-reference-driven-entry.md +238 -238
- package/docs/reports/v0.10-batch6-coding-agent-handoff.md +85 -85
- package/docs/reports/v0.10-batch7-correction-review-acceptance.md +145 -145
- package/docs/reports/v0.10-batch8-integrated-acceptance.md +109 -109
- package/docs/reports/v0.10-implementation-completeness-documentation-reconciliation.md +344 -344
- package/docs/reports/v0.10-pre-release-readiness.md +120 -120
- package/docs/reports/v0.10-release-preparation.md +70 -70
- package/docs/reports/v0.10.1-project-check-baseline-context-implementation.md +86 -0
- package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -243
- package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -497
- package/docs/reports/v0.7-pre-release-readiness.md +337 -337
- package/docs/reports/v0.7-reference-binding-prompt5.md +223 -223
- package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -234
- package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -222
- package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -216
- package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -151
- package/docs/reports/v0.7-reference-regions-prompt2.md +195 -195
- package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -217
- package/docs/reports/v0.7-release-prep.md +423 -423
- package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -279
- package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -233
- package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -279
- package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -247
- package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -741
- package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -128
- package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -223
- package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -687
- package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -232
- package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -278
- package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -114
- package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -170
- package/docs/reports/v0.9-architecture-retrieval.md +14 -37
- package/docs/reports/v0.9-final-pre-release-readiness.md +209 -209
- package/docs/reports/v0.9-final-readiness-corrections.md +530 -530
- package/docs/reports/v0.9-pre-release-readiness.md +169 -169
- package/docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md +359 -359
- package/docs/reports/v0.9.1-batch2-hard-gate-validation-integration.md +262 -262
- package/docs/reports/v0.9.1-pre-release-readiness.md +206 -206
- package/package.json +59 -59
|
@@ -1,247 +1,247 @@
|
|
|
1
|
-
# v0.8 Batch 2 — Evidence Indexing, Canonical Readers, and Lazy Data Boundary — Implementation Report
|
|
2
|
-
|
|
3
|
-
## 1. Starting state
|
|
4
|
-
|
|
5
|
-
- Starting branch: `master`
|
|
6
|
-
- Starting HEAD: `3fb9df9db9335bb1efbd1cd90c00dfb0dc3cd41d` ("feat: add v0.8 viewer runtime and PWA foundation")
|
|
7
|
-
- `origin/master` HEAD after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
-
- `git rev-list --left-right --count origin/master...HEAD`: `0 1` — local is exactly one commit (Batch 1) ahead of origin, no divergence. Per task §4, no pull/reset/merge was performed.
|
|
9
|
-
- Starting `git status --short`: clean.
|
|
10
|
-
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
11
|
-
|
|
12
|
-
## 2. Batch 1 report inspected
|
|
13
|
-
|
|
14
|
-
Read the complete local `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` (not just its console summary). Confirmed and reused without modification:
|
|
15
|
-
|
|
16
|
-
- host `127.0.0.1`, fixed default port `4319` (`src/viewerServer/port.ts`);
|
|
17
|
-
- server owner module `src/viewerServer/httpServer.ts` (`createViewerServer`/`handleRequest`) and application seam `src/viewerServer/viewerService.ts` (`startViewer`);
|
|
18
|
-
- `/api/status` as the sole Batch 1 route, with an existing `pathname.startsWith('/api/')` fallback that Batch 2 routes were inserted before;
|
|
19
|
-
- CLI dispatch ownership (`src/cli.ts` `runViewCommand`/`parseViewArgs`) - unchanged this batch;
|
|
20
|
-
- PWA cache boundary: `viewer/vite.config.ts`'s `workbox.navigateFallbackDenylist: [/^\/api\//]`, which - being a prefix match on `/api/` - already covers every new Batch 2 route without modification;
|
|
21
|
-
- generated-path convention (`WORKFLOW_ROOT` sibling directory, `npm_config_cache`/`VITE_CACHE_DIR` redirection);
|
|
22
|
-
- Batch 1's own unresolved risks (install-prompt "available" branch untested in headless Chromium; no live service-worker execution test) - unrelated to Batch 2, not re-litigated.
|
|
23
|
-
|
|
24
|
-
No contradiction between the report and the task's Batch 1 summary was found.
|
|
25
|
-
|
|
26
|
-
## 3. Frozen planning authority inspected
|
|
27
|
-
|
|
28
|
-
Read (or re-confirmed already-cached knowledge of, where noted) in order:
|
|
29
|
-
|
|
30
|
-
1. `docs/DOCUMENTATION_PRESERVATION_POLICY.md` (previously read, unchanged)
|
|
31
|
-
2. `docs/PROJECT_MILESTONES.md` (Milestone 8, previously read in full)
|
|
32
|
-
3. `docs/ROADMAP.md` (v0.8 section, previously read)
|
|
33
|
-
4. `docs/plans/v0.8-implementation-plan.md` Batch 2 section (`### Batch 2 — Evidence indexing, canonical readers, and lazy data boundary`) plus cross-batch invariants (§7)
|
|
34
|
-
5. `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` (§2 above)
|
|
35
|
-
6. `docs/ARCHITECTURE.md` (relevant sections, then edited)
|
|
36
|
-
7. `docs/CONTRACTS.md` (fully, in two paginated reads - artifact-kind/schema-version constants, directory/manifest-filename conventions, and the v0.7 external-reference media-ownership contract)
|
|
37
|
-
8. `docs/COMMANDS.md` (`view` section, then edited)
|
|
38
|
-
9. `docs/WORKFLOWS.md` (bounded-agent-context and external-reference workflow sections)
|
|
39
|
-
10. `package.json`, `src/viewerServer/httpServer.ts`, `src/viewerServer/viewerService.ts`, `src/viewerServer/port.ts`
|
|
40
|
-
11. `src/artifacts/artifactReader.ts`, `comparisonArtifactReader.ts`, `externalReferenceArtifactReader.ts`, `frontendContractArtifactReader.ts`, `frontendContractEvaluationArtifactReader.ts`, `types.ts`
|
|
41
|
-
12. `src/artifacts/externalReferenceArtifactWriter.ts` (directory/media-ownership shape)
|
|
42
|
-
13. `src/domain/schema.ts`, `comparison.ts`, `frontendContracts.ts`, `frontendContractEvaluationArtifact.ts`, `externalReference.ts`, `boundedAgentContext.ts`, `evidence.ts`, `completion.ts`
|
|
43
|
-
14. `tests/unit/cliFrontendContracts.test.ts` (fixture-construction pattern reused for Batch 2 test fixtures), `tests/unit/diagnostics.test.ts`, `tests/unit/externalReferenceImageFixtures.ts`
|
|
44
|
-
|
|
45
|
-
Confirmed the frozen plan is unchanged and Batch 2's title/scope match the task exactly.
|
|
46
|
-
|
|
47
|
-
## 4. my-dev-kit retrieval
|
|
48
|
-
|
|
49
|
-
Unlike Batch 1, this step was **run**, not skipped. Index built successfully:
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
npx @dailephd/my-dev-kit@latest index --root . --src src --src tests --src viewer \
|
|
53
|
-
--out "<WORKFLOW_ROOT>\my-dev-kit-index" --call-graph --json
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Result: `status: "complete"`, 73 files indexed. Ran all five required searches (reader ownership, artifact-write ownership, bounded-agent-context persistence, screenshot/media ownership, viewer server routes, and artifact-kind/schema-version constants). Every result matched direct source inspection exactly - in particular, the `BoundedAgentContextArtifact` search returned only `src/domain/boundedAgentContext.ts` and application-service files, **no** `boundedAgentContextArtifactReader.ts`/`Writer.ts` - corroborating (not merely asserting) that no disk reader/writer exists for that family. No `lookup`/`source`/`slice` follow-up was needed since search results were unambiguous. `MY_DEV_KIT_INDEX` = `Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-02\my-dev-kit-index`.
|
|
57
|
-
|
|
58
|
-
## 5. Evidence-family inventory
|
|
59
|
-
|
|
60
|
-
| Family | Kind constant | Schema | Persisted? | Writer | Reader (existing) | Discriminant | Media owned/referenced |
|
|
61
|
-
|---|---|---|---|---|---|---|---|
|
|
62
|
-
| Observation | `my-frontend-observer/observation` | `1.2.0` | Yes | `artifactWriter.ts` | `artifactReader.ts#readObservationArtifact` | - | owns `screenshot.png` (via `screenshot: EvidenceField<{path}>`) |
|
|
63
|
-
| Comparison | `my-frontend-observer/comparison` | `1.0.0` | Yes | `comparisonArtifactWriter.ts` | `comparisonArtifactReader.ts#readComparisonArtifact` | - | none (references `before`/`after` observation ids only) |
|
|
64
|
-
| Persistent baseline contract | `my-frontend-observer/frontend-contract` | `1.0.0` | Yes | `frontendContractArtifactWriter.ts#writePersistentBaselineContract` | `frontendContractArtifactReader.ts#readPersistentBaselineContract` | `contractClass: 'baseline'` | none |
|
|
65
|
-
| Per-change contract | `my-frontend-observer/frontend-contract` | `1.0.0` | Yes | `...#writePerChangeContract` | `...#readPerChangeContract` | `contractClass: 'change'` | none |
|
|
66
|
-
| Contract evaluation | `my-frontend-observer/frontend-contract-evaluation` | `1.0.0` | Yes | `frontendContractEvaluationArtifactWriter.ts` | `frontendContractEvaluationArtifactReader.ts` | - | none |
|
|
67
|
-
| External reference (imported) | `my-frontend-observer/external-reference` | `1.0.0` | Yes | `externalReferenceArtifactWriter.ts` | `externalReferenceArtifactReader.ts#readExternalReferenceArtifact` | `lifecycle.state: 'imported'` | owns `image.<ext>` (bare filename) |
|
|
68
|
-
| External reference (approved) | `my-frontend-observer/external-reference` | `1.0.0` | Yes | same | same | `lifecycle.state: 'approved'` | references, never copies, the imported artifact's image via `sourceReference.{referenceId, image}` |
|
|
69
|
-
| Bounded agent context | `my-frontend-observer/bounded-agent-context` | `1.0.0` | **No** | none | none | - | n/a |
|
|
70
|
-
|
|
71
|
-
Every family (all filename `manifest.json`, one artifact directory per instance) belongs in Batch 2 discovery **except** bounded-agent-context, which - per `docs/CONTRACTS.md` "v0.6 bounded agent context and correlation contract" ("there is no disk artifact writer/reader... it is a pure programmatic contract and derivation layer") and confirmed by my-dev-kit search (§4) - has no persisted filesystem representation to discover. Per task §8/§21, no persistence was invented for it; a manifest declaring that kind is classified `unrecognized-kind` (test: `evidenceDiscovery.test.ts` "a bounded-agent-context-kind manifest is treated as unrecognized").
|
|
72
|
-
|
|
73
|
-
## 6. Existing readers reused (no new readers were needed)
|
|
74
|
-
|
|
75
|
-
All five persisted families already had a canonical reader before this batch (`readObservationArtifact`, `readComparisonArtifact`, `readPersistentBaselineContract`, `readPerChangeContract`, `readFrontendContractEvaluationArtifact`, `readExternalReferenceArtifact`) - confirmed by direct inspection (§3) and my-dev-kit search (§4). **No new reader was added.** `src/viewerServer/evidence/classify.ts` calls these six functions unchanged; it never re-implements JSON parsing or structural validation.
|
|
76
|
-
|
|
77
|
-
## 7. Canonical validators reused
|
|
78
|
-
|
|
79
|
-
Every classification decision for a "supported" or "invalid-structure" result comes from the family's own existing validator, invoked transitively by its reader (`isValidObservationArtifact`, `isValidComparisonArtifact`, `isValidPersistentBaselineContract`, `isValidPerChangeContract`, `isValidFrontendContractEvaluationArtifact`, `isValidExternalReferenceArtifact`). No second/parallel validator was introduced anywhere in `src/viewerServer/evidence/`. For the shared `frontend-contract` kind, `classify.ts` tries `readPersistentBaselineContract` then `readPerChangeContract` in sequence - the **existing validators**, not classifier-owned logic, decide which (if either) a candidate actually is.
|
|
80
|
-
|
|
81
|
-
## 8. Index bounds selected (and rationale)
|
|
82
|
-
|
|
83
|
-
Defined in `src/viewerServer/evidence/limits.ts`, chosen after inspecting that every current writer produces a shallow `<outputLocation>/<id>/manifest.json` shape (§5) rather than a deep tree:
|
|
84
|
-
|
|
85
|
-
- `MAX_DISCOVERY_DEPTH = 6` (typical real shape needs depth 2; generous slack for a nested `--output`, still bounded);
|
|
86
|
-
- `MAX_DIRECTORIES_VISITED = 2000`;
|
|
87
|
-
- `MAX_MANIFEST_CANDIDATES = 1000` (candidate `manifest.json` files classified);
|
|
88
|
-
- `MAX_INDEX_RECORDS = 500` (metadata records returned in one `/api/index` response);
|
|
89
|
-
- `MAX_MANIFEST_CANDIDATE_BYTES = 2,000,000` - a candidate exceeding this is never read into memory; classified `unreadable` instead. Real manifests are small (identity/geometry/config JSON only; screenshots/images are always separate sibling files - §5), so this is generous headroom, not a tight fit.
|
|
90
|
-
|
|
91
|
-
`discoverManifests` opens **only** files literally named `manifest.json` - no other filename is ever read or classified, regardless of content (even valid JSON), satisfying "do not treat every `manifest.json` as valid Observer evidence... do not treat arbitrary JSON files as viewer artifacts" without needing a broader allow/deny list.
|
|
92
|
-
|
|
93
|
-
## 9. Symlink/junction policy
|
|
94
|
-
|
|
95
|
-
`discovery.ts#walk` checks `dirent.isSymbolicLink()` on every entry (file or directory) via `readdir(dir, {withFileTypes:true})` and skips it entirely - never followed, never opened. Verified with a real symlinked directory pointing outside the evidence root (`evidenceDiscovery.test.ts` "never follows a directory symlink out of the evidence root"; the test degrades gracefully rather than failing if symlink creation requires elevated privileges in a given Windows configuration - confirmed working in this environment). Since symlinks are never followed, no separate `realpath`-based re-containment check was needed for the walk itself; `resolveContainedDir`/`resolveContainedFile` (§13) provide that containment guarantee independently for handle-driven (not walk-driven) access.
|
|
96
|
-
|
|
97
|
-
## 10. Viewer handle design
|
|
98
|
-
|
|
99
|
-
A handle is `<family-slug>:<percent-encoded root-relative directory path>` (`evidence/handles.ts`) - e.g. `observation:observations%2Fsmoke-obs-before`. It is "server-issued" only in the sense that it is exactly what discovery/classification already produced; it grants no filesystem access by itself. **Every** route that accepts a handle (`/api/artifacts/<handle>`, `/api/media/<handle>/<role>`) independently decodes it, re-resolves it against the evidence root via `resolveContainedDir` (re-checking containment), and re-classifies that one candidate through the same canonical-reader dispatch the index uses - it never trusts a cached record or a client-supplied path string. A handle whose backing directory no longer exists, or whose re-classified family no longer matches what the handle claims, fails closed as "unknown handle" (404), never served as stale.
|
|
100
|
-
|
|
101
|
-
## 11. API routes and semantics
|
|
102
|
-
|
|
103
|
-
| Route | Method | Semantics |
|
|
104
|
-
|---|---|---|
|
|
105
|
-
| `GET /api/status` | GET/HEAD | Unchanged from Batch 1. |
|
|
106
|
-
| `GET /api/index` | GET/HEAD | Bounded, metadata-only, deterministic (rebuilt fresh every call - no persistent cache). |
|
|
107
|
-
| `GET /api/artifacts/<handle>` | GET/HEAD | Known handle only; one canonical-reader read; on demand; no schema upgrade; no mutation. `200` supported / `404` unknown handle / `409` handle resolves but is not currently loadable (unsupported-version, invalid-structure, malformed-json, unreadable - carries the same honest `metadata` the index would show). |
|
|
108
|
-
| `GET /api/media/<handle>/<role>` | GET/HEAD | Known handle + known role only; root/artifact-contained; on demand; streamed. `200` / `404` for any failure (unknown handle, unknown/inapplicable role, missing file). |
|
|
109
|
-
|
|
110
|
-
All four reject every write method (`POST`/`PUT`/`PATCH`/`DELETE`) with `405 Method Not Allowed` (enforced once, ahead of all routing, in `handleRequest` - unchanged Batch 1 mechanism now covers the new routes too; verified in `viewerEvidenceServer.test.ts`).
|
|
111
|
-
|
|
112
|
-
## 12. Viewer adapter architecture
|
|
113
|
-
|
|
114
|
-
`src/viewerServer/evidence/projection.ts`:
|
|
115
|
-
|
|
116
|
-
- `EvidenceMetadataRecord` - the `/api/index` shape: `handle`, `family`, `supportState`, `relativeDir`, and (only when meaningful for that family/state) `logicalId`, `schemaVersion`, `foundSchemaVersion`, `producerVersion`, `completion`, `lifecycleState`, `contractClass`, `overallVerdict`, `comparability`, `media` (availability summary only, never bytes), `relatedIds`, `message`. Verified to exclude full-payload fields (`targetEvidence`, `pageEvidence`, `requestConfig`) via a direct serialization assertion.
|
|
117
|
-
- `EvidenceArtifactDetail` - the `/api/artifacts/<handle>` shape: the already-validated domain object wrapped with `handle`/`family`. Selects/wraps existing canonical fields only; derives nothing new, recomputes nothing, persists nothing (no `viewer.json`/viewer-manifest/cache artifact of any kind exists anywhere in this batch).
|
|
118
|
-
|
|
119
|
-
Both are ephemeral: constructed fresh per request, held only in memory for the duration of that request/response.
|
|
120
|
-
|
|
121
|
-
## 13. Absolute-path exposure policy
|
|
122
|
-
|
|
123
|
-
Metadata records expose only a root-relative `relativeDir` (POSIX-normalized), never an absolute filesystem path. `/api/status`'s pre-existing `root` field (Batch 1, unchanged) remains the only place an absolute path is echoed back, and only as an opaque display string the server itself supplied - never accepted as input from the browser.
|
|
124
|
-
|
|
125
|
-
## 14. Media ownership/resolution behavior
|
|
126
|
-
|
|
127
|
-
`src/viewerServer/evidence/mediaResolver.ts` recognizes exactly three roles: `screenshot` (observation only), `image` (imported external reference only), `source-image` (approved external reference only) - any other role, or a role requested against a family it doesn't apply to, is rejected (`404`). Every resolved path goes through `resolveContainedFile`, which additionally rejects any filename containing a path separator outright (every real media reference is contractually a bare filename - `docs/CONTRACTS.md` "v0.7 Prompt 1" `ExternalReferenceImageReference.path`).
|
|
128
|
-
|
|
129
|
-
## 15. Approved external-reference source-image behavior
|
|
130
|
-
|
|
131
|
-
Confirmed via `docs/CONTRACTS.md`/`externalReference.ts` (§3/§5) and proven by a real fixture (`writeExternalReferencePairFixture`, using the actual `importExternalReference`/`approveExternalReference` application services): an approved artifact's directory contains **only** `manifest.json` - no image file. `source-image` resolution reads the approved artifact's `sourceReference.referenceId`, then calls `evidence/index.ts#findImportedReferenceDir` - a bounded search (reusing the same discovery+classify pipeline, capped at `MAX_INDEX_RECORDS`) for the **imported** artifact whose own `referenceId` matches - and only then resolves `sourceReference.image.path` against *that* artifact's real directory. Never assumes co-location. Two tests prove this exactly: "resolves an approved reference's source-image through the owning imported artifact, never assuming co-location" (success case, real cross-artifact resolution) and "an approved reference whose owning imported artifact is absent from the root reports missing media honestly" (the imported artifact deliberately left out of the root - `404`, not a crash or a fabricated image). The full built-CLI smoke test (§20) additionally proves this against the real compiled server.
|
|
132
|
-
|
|
133
|
-
## 16. Unsupported-version behavior
|
|
134
|
-
|
|
135
|
-
`classify.ts` compares the candidate's `schemaVersion` against the current canonical constant (`SCHEMA_VERSION`, `COMPARISON_SCHEMA_VERSION`, `CONTRACT_SCHEMA_VERSION`, `EVALUATION_SCHEMA_VERSION`, `EXTERNAL_REFERENCE_SCHEMA_VERSION`) **before** attempting a canonical read, so an unsupported version never even reaches (and is never silently accepted by) the reader/validator pair pinned to the current schema. `/api/index` reports `supportState: 'unsupported-version'` plus both the found and supported version strings; `/api/artifacts/<handle>` refuses to load it (`409`, carrying that same honest metadata) - never coerced into a fabricated current-shape domain object. Proven with a real fixture (`writeUnsupportedVersionManifest`: a genuine `my-frontend-observer/observation` kind at schema `99.0.0`) at the unit level and in the real-Chromium/CLI smoke.
|
|
136
|
-
|
|
137
|
-
## 17. Malformed/unavailable/missing behavior
|
|
138
|
-
|
|
139
|
-
Six independent, honestly distinguished states (`ViewerSupportState` in `classify.ts`): `supported`, `unsupported-version`, `invalid-structure` (kind+version match but the canonical validator itself rejects it), `unrecognized-kind` (no known `artifactKind`, including the bounded-agent-context case), `malformed-json` (not valid JSON at all), `unreadable` (stat/read failure or over the size bound). None is ever silently dropped or reinterpreted as `supported`; one malformed/unrelated candidate never erases valid neighboring evidence (`evidenceDiscovery.test.ts`: 6 real pipeline artifacts + 1 malformed all appear in one index response). Missing media is reported per-role (`available: false` with an honest `reason`) rather than as a whole-artifact failure - a genuinely deleted `screenshot.png` still yields a `supported` observation record whose `media` summary honestly says `unavailable`.
|
|
140
|
-
|
|
141
|
-
## 18. PWA cache verification
|
|
142
|
-
|
|
143
|
-
No `vite.config.ts`/service-worker configuration change was needed: every new route lives under `/api/`, already covered by Batch 1's `navigateFallbackDenylist: [/^\/api\//]`. Extended `tests/unit/viewerPwaBuild.test.ts` with an explicit Batch 2 assertion against the **real built** `sw.js`: still exactly one `registerRoute` call after the server-side additions, and the precache manifest contains no `/api/index`, `/api/artifacts`, or `/api/media` entry.
|
|
144
|
-
|
|
145
|
-
## 19. Batch 2 UI changes
|
|
146
|
-
|
|
147
|
-
`viewer/src/hooks/useEvidenceIndex.ts` (fetches `/api/index`), `useArtifactDetail.ts` (fetches `/api/artifacts/<handle>` only when a handle is selected - never eagerly), `components/EvidenceList.tsx` (bounded list, one item per record, honest support-state label per item), `components/ArtifactPreview.tsx` (loading/loaded/error/unsupported states; a bounded raw-JSON preview of the loaded artifact - explicitly *not* a real visualization). `App.tsx` wires these into the existing Batch 1 shell regions (nav = list, workspace = preview, details aside = selected record's own already-fetched metadata fields - no additional request). No screenshot rendering, SVG overlay, or comparison/contract/reference visualization exists anywhere in `viewer/src/` (verified by a real-Chromium test asserting zero `<img>`/`<svg>` elements after loading a real observation).
|
|
148
|
-
|
|
149
|
-
## 20. Files created
|
|
150
|
-
|
|
151
|
-
- `src/viewerServer/evidence/limits.ts`, `discovery.ts`, `classify.ts`, `handles.ts`, `pathSafety.ts`, `projection.ts`, `index.ts`, `mediaResolver.ts`
|
|
152
|
-
- `tests/support/evidenceFixtures.ts` (shared real-evidence fixture builders, reusing the existing writer/application-service functions - mirrors `tests/unit/cliFrontendContracts.test.ts`'s established construction pattern)
|
|
153
|
-
- `tests/unit/evidenceDiscovery.test.ts`, `viewerEvidenceServer.test.ts`
|
|
154
|
-
- `tests/browser/viewerEvidenceShell.test.ts`
|
|
155
|
-
- `viewer/src/hooks/useEvidenceIndex.ts`, `useArtifactDetail.ts`
|
|
156
|
-
- `viewer/src/components/EvidenceList.tsx`, `ArtifactPreview.tsx`
|
|
157
|
-
- `docs/reports/v0.8-evidence-index-readers-batch2.md` (this file)
|
|
158
|
-
|
|
159
|
-
## 21. Files modified
|
|
160
|
-
|
|
161
|
-
- `src/viewerServer/httpServer.ts` - added `/api/index`, `/api/artifacts/<handle>`, `/api/media/<handle>/<role>` routing plus shared `writeJsonBody`/`writeJsonError`/`streamFile`/`decodeURIComponentSafe` helpers; existing `/api/status` and static-asset serving unchanged.
|
|
162
|
-
- `viewer/src/App.tsx` - wires the new hooks/components into the existing shell regions.
|
|
163
|
-
- `viewer/src/styles/index.css` - additive rules for the evidence list/preview/details UI.
|
|
164
|
-
- `tests/browser/viewerShell.test.ts` - one pre-existing Batch 1 assertion (`toContain('Evidence navigation will appear here')`, a *static placeholder* string) legitimately no longer holds now that the nav region is live; updated to assert the new, honest "no recognized evidence" message for an empty root - the same kind of sanctioned evolution as Batch 1's own `TST-401` update.
|
|
165
|
-
- `tests/unit/viewerPwaBuild.test.ts` - added the explicit Batch 2 cache-boundary assertion (§18).
|
|
166
|
-
- `docs/ARCHITECTURE.md` - new "v0.8 Batch 2" section.
|
|
167
|
-
- `docs/COMMANDS.md` - `view` section updated to describe the new routes/behavior.
|
|
168
|
-
|
|
169
|
-
## 22. A production bug found and fixed by real-CLI smoke testing
|
|
170
|
-
|
|
171
|
-
`src/viewerServer/evidence/pathSafety.ts#resolveContainedDir` originally compared a `path.resolve()`-normalized candidate path against the **un-normalized** `root` string. Every unit test's `root` came from `path.join(tmpdir(), ...)`, which Node already normalizes to the platform separator, masking the bug entirely - all 19 initial `viewerEvidenceServer.test.ts` tests passed. The real built-CLI smoke test (§23), run with `--root` supplied using forward slashes (as a user typing a path in a shell commonly would, even on Windows), reproduced it immediately: every artifact/media request returned `404 unknown handle` even for evidence that genuinely existed. Root-caused via direct `node -e` reproduction against the compiled `dist` output, fixed by resolving `root` itself before the prefix comparison, and covered by a new dedicated regression test (`viewerEvidenceServer.test.ts` "root path normalization" describe block) that deliberately constructs a forward-slash root and asserts a real handle still resolves. This is exactly the kind of defect the task's mandated built-CLI smoke step (§34) exists to catch, and it did.
|
|
172
|
-
|
|
173
|
-
## 23. Validation results
|
|
174
|
-
|
|
175
|
-
| Command | Result |
|
|
176
|
-
|---|---|
|
|
177
|
-
| `npm run typecheck` | **PASS** (zero errors, both `tsconfig.json` and `viewer/tsconfig.json`) |
|
|
178
|
-
| `npm run lint` | **PASS** (zero errors/warnings) |
|
|
179
|
-
| `npm test` | **PASS** - 1071/1071 tests, 56/56 files |
|
|
180
|
-
| `npm run build` | **PASS** - unchanged Node/library output plus `dist/viewerServer/evidence/**` and the rebuilt `dist/viewer/**` PWA |
|
|
181
|
-
| `npm run check:docs` | **PASS** - "Documentation check passed (17 required files)." |
|
|
182
|
-
| `npm run test:browser` | **PASS** - 132/132 tests, 12/12 files (real Chromium; run in full, not skipped, per task §33's instruction since this batch directly changes the browser-facing data boundary) |
|
|
183
|
-
| `git diff --check` | **PASS** - no whitespace errors (only expected LF→CRLF line-ending notices) |
|
|
184
|
-
|
|
185
|
-
## 24. Built viewer Batch 2 smoke (§34)
|
|
186
|
-
|
|
187
|
-
Fixture: a real evidence root built via a one-off script (not committed; lives under `WORKFLOW_ROOT\tmp`) that calls the actual compiled `dist/artifacts/*Writer.js` and `dist/application/*Service.js` modules directly - the same real writers/services the CLI itself uses - to produce two real observations, a real comparison, a real baseline contract, a real per-change contract, a real evaluation, a real imported external reference (with a real image file), and a real approved external reference, plus a malformed-JSON candidate, an unsupported-future-schema-version candidate, and one unrelated `README.txt`, all under `WORKFLOW_ROOT\smoke\evidence-root`.
|
|
188
|
-
|
|
189
|
-
Command: `node dist/cli.js view --root "<WORKFLOW_ROOT>\smoke\evidence-root" --port 4319 --no-open`
|
|
190
|
-
|
|
191
|
-
All required checks (§34), against the real running built server:
|
|
192
|
-
|
|
193
|
-
- `/api/status` → `200`, correct root echoed.
|
|
194
|
-
- `/api/index` → `200`, all 9 real evidence records present with correct `family`/`supportState`, plus the malformed and unsupported-version candidates shown honestly; response body contains no full-artifact fields (`targetEvidence` etc.) and no `README` reference.
|
|
195
|
-
- Selected supported artifact (`observation:observations%2Fsmoke-obs-before`) loaded on demand via `/api/artifacts/<handle>` → `200` with its full payload.
|
|
196
|
-
- Selected media loaded on demand: observation `screenshot` → `200 image/png`; imported reference `image` → `200 image/png`; **approved reference `source-image` → `200 image/png`, resolved through the separate imported artifact's directory** (§15/§22).
|
|
197
|
-
- Unsupported-version and malformed candidates both correctly refused at `/api/artifacts/<handle>` → `409` (not fabricated as loaded).
|
|
198
|
-
- Unrelated `README.txt` never appears in `/api/index` and is not directly servable (`GET /README.txt` → `404`).
|
|
199
|
-
- Path-traversal attempt (`/api/artifacts/observation:..%2F..%2Fetc`) → `404`.
|
|
200
|
-
- Absolute-path-shaped handle attempt → `404`.
|
|
201
|
-
- PWA still loads: `/` → `200`, `/manifest.webmanifest` → `200`, `/sw.js` → `200`.
|
|
202
|
-
- `netstat` confirmed the listener bound to `127.0.0.1:4319` only (never `0.0.0.0`).
|
|
203
|
-
- Server located by its real PID via `netstat` and terminated with `taskkill /F`; a follow-up `netstat` confirmed the port was released and no server process remained.
|
|
204
|
-
- A post-run directory listing of the smoke evidence root shows exactly the fixture's own files - no stray `.tmp-*` write-in-progress directories, no modified/deleted files.
|
|
205
|
-
|
|
206
|
-
**Result: PASS.** Logs retained under `WORKFLOW_ROOT\logs\` (`smoke-server.log`, `smoke-server-2.log` [post-fix rerun], `index-response.json`, `index-response-2.json`, `smoke-checks-final.log`).
|
|
207
|
-
|
|
208
|
-
## 25. Existing command regression check
|
|
209
|
-
|
|
210
|
-
`observe`, `compare`, `approve-baseline`, `save-change-contract`, `evaluate-contract`, `import-reference`, `approve-reference`, `evaluate-reference-fidelity`, Batch 1 `view` startup, PWA shell, fixed host/port behavior, and the PWA cache boundary all remain covered by their existing, unmodified test suites (all 1071 unit + 132 browser tests pass - §23). Canonical v0.1-v0.7 semantics were not touched anywhere in this batch's diff; `src/domain/`, `src/artifacts/*Writer.ts`, and every existing reader are byte-for-byte unchanged.
|
|
211
|
-
|
|
212
|
-
## 26. Generated path inventory
|
|
213
|
-
|
|
214
|
-
| Path | Disposition |
|
|
215
|
-
|---|---|
|
|
216
|
-
| `WORKFLOW_ROOT\cache\npm` | Retained (npm cache from the my-dev-kit-index `npx` invocation; harmless, reusable) |
|
|
217
|
-
| `WORKFLOW_ROOT\tmp\vite-cache`, `WORKFLOW_ROOT\tmp\build-smoke-evidence.mjs` | Retained (build cache dir was empty again, same as Batch 1 - Vite build mode doesn't populate it; the smoke-fixture script is dev/readiness tooling only, not committed) |
|
|
218
|
-
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence - §24) |
|
|
219
|
-
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained (real evidence fixture tree built for the smoke test - §24) |
|
|
220
|
-
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained (successful index + cache-metadata from §4) |
|
|
221
|
-
| `WORKFLOW_ROOT\{fixtures,candidate,pack}` | Retained, empty/unused this batch (no `npm pack`/candidate-install step was needed since no packaging-boundary change occurred beyond the existing `dist` allowlist) |
|
|
222
|
-
| Repo-root `dist/` | Ordinary build output (gitignored); rebuilt cleanly by `scripts/clean.mjs` on every `npm run build` |
|
|
223
|
-
| Pre-existing repo-root `.my-dev-kit*`/`baselines`/`comparisons`/`contracts`/`evaluations`/`observations` | Pre-existing, empty, untouched (same finding as the Batch 1 report) |
|
|
224
|
-
|
|
225
|
-
`Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-01` was never targeted by any command in this session (verified) - preserved exactly as Batch 1 left it.
|
|
226
|
-
|
|
227
|
-
## 27. Repository pollution check
|
|
228
|
-
|
|
229
|
-
`git status --short` before staging showed only the seven modified + eight new Batch-2-owned paths listed in §20/§21 (plus their containing directories). No unexpected file or directory appeared anywhere in the repository as a result of this batch's work.
|
|
230
|
-
|
|
231
|
-
## 28. Skipped work / deviations
|
|
232
|
-
|
|
233
|
-
- None. Every task step (my-dev-kit retrieval, all six required test categories, the full validation chain including `test:browser`, and the built-CLI smoke) was executed, not skipped or substituted.
|
|
234
|
-
|
|
235
|
-
## 29. Remaining uncovered risks
|
|
236
|
-
|
|
237
|
-
- **`findImportedReferenceDir` re-walks the evidence tree per `source-image` request** rather than reusing a cached index (by design - no persistent cache exists, per task §30). For an evidence root at the bound edge (near `MAX_MANIFEST_CANDIDATES`), an approved-reference-heavy workload would re-pay that walk cost on every source-image request. Not a correctness risk; a potential future performance consideration only if real usage ever approaches the bound (task §30 explicitly says to report rather than pre-optimize).
|
|
238
|
-
- **The bounded-agent-context "unrecognized-kind" classification is asserted only at the unit level**, not against a real persisted fixture (none exists to build, by design - §5). The behavior is exercised with a hand-written manifest declaring that kind, which is the most realistic proof achievable without inventing persistence for a family that has none.
|
|
239
|
-
- Batch 1's previously reported risks (install-prompt "available" branch, live service-worker execution) remain unresolved and out of this batch's scope.
|
|
240
|
-
|
|
241
|
-
## 30. Out-of-scope confirmation
|
|
242
|
-
|
|
243
|
-
Confirmed absent from this batch's diff: target SVG overlays, screenshot geometry rendering, runtime target inspection UI, relationship/scroll visualization, before/after visual comparison, contract/change-scope visualization, external-reference/candidate side-by-side visual inspection, region/target overlays, explicit-binding cross-selection, zoom/pan, synchronized lock, on-demand fidelity computation, bounded-agent-context visual inspection, correlation UI, arbitrary filesystem navigation, annotation, region/requirement authoring, source editing, automatic binding/target discovery, pixel-diff scoring, computer vision, cloud hosting, database, authentication, collaboration. `ArtifactPreview.tsx`'s raw-JSON preview is explicitly not a visualization (no image, no SVG, no geometry rendering - proven by the zero-`<img>`/zero-`<svg>` browser assertion, §19).
|
|
244
|
-
|
|
245
|
-
## 31. Final verdict
|
|
246
|
-
|
|
247
|
-
Batch 2 ("Evidence indexing, canonical readers, and lazy data boundary") is implemented and independently validated: a built candidate's viewer can enumerate real recognized evidence from bounded metadata, load one selected artifact on demand through its existing canonical validation boundary, load its owned/referenced media safely (including the cross-artifact approved-reference case), and visibly distinguish malformed/unsupported/missing evidence rather than silently dropping it - proven at the unit, real-Chromium, and real-built-CLI-smoke levels, with one genuine cross-platform path-handling defect found and fixed along the way. No Batch 3+ visualization, no v0.9 annotation, and no release/publication action was taken. Package version remains `0.7.0`.
|
|
1
|
+
# v0.8 Batch 2 — Evidence Indexing, Canonical Readers, and Lazy Data Boundary — Implementation Report
|
|
2
|
+
|
|
3
|
+
## 1. Starting state
|
|
4
|
+
|
|
5
|
+
- Starting branch: `master`
|
|
6
|
+
- Starting HEAD: `3fb9df9db9335bb1efbd1cd90c00dfb0dc3cd41d` ("feat: add v0.8 viewer runtime and PWA foundation")
|
|
7
|
+
- `origin/master` HEAD after `git fetch`: `a1de8ac01e1367b60021cb04226f56369fa2debb`
|
|
8
|
+
- `git rev-list --left-right --count origin/master...HEAD`: `0 1` — local is exactly one commit (Batch 1) ahead of origin, no divergence. Per task §4, no pull/reset/merge was performed.
|
|
9
|
+
- Starting `git status --short`: clean.
|
|
10
|
+
- Package version confirmed `0.7.0` throughout; never bumped.
|
|
11
|
+
|
|
12
|
+
## 2. Batch 1 report inspected
|
|
13
|
+
|
|
14
|
+
Read the complete local `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` (not just its console summary). Confirmed and reused without modification:
|
|
15
|
+
|
|
16
|
+
- host `127.0.0.1`, fixed default port `4319` (`src/viewerServer/port.ts`);
|
|
17
|
+
- server owner module `src/viewerServer/httpServer.ts` (`createViewerServer`/`handleRequest`) and application seam `src/viewerServer/viewerService.ts` (`startViewer`);
|
|
18
|
+
- `/api/status` as the sole Batch 1 route, with an existing `pathname.startsWith('/api/')` fallback that Batch 2 routes were inserted before;
|
|
19
|
+
- CLI dispatch ownership (`src/cli.ts` `runViewCommand`/`parseViewArgs`) - unchanged this batch;
|
|
20
|
+
- PWA cache boundary: `viewer/vite.config.ts`'s `workbox.navigateFallbackDenylist: [/^\/api\//]`, which - being a prefix match on `/api/` - already covers every new Batch 2 route without modification;
|
|
21
|
+
- generated-path convention (`WORKFLOW_ROOT` sibling directory, `npm_config_cache`/`VITE_CACHE_DIR` redirection);
|
|
22
|
+
- Batch 1's own unresolved risks (install-prompt "available" branch untested in headless Chromium; no live service-worker execution test) - unrelated to Batch 2, not re-litigated.
|
|
23
|
+
|
|
24
|
+
No contradiction between the report and the task's Batch 1 summary was found.
|
|
25
|
+
|
|
26
|
+
## 3. Frozen planning authority inspected
|
|
27
|
+
|
|
28
|
+
Read (or re-confirmed already-cached knowledge of, where noted) in order:
|
|
29
|
+
|
|
30
|
+
1. `docs/DOCUMENTATION_PRESERVATION_POLICY.md` (previously read, unchanged)
|
|
31
|
+
2. `docs/PROJECT_MILESTONES.md` (Milestone 8, previously read in full)
|
|
32
|
+
3. `docs/ROADMAP.md` (v0.8 section, previously read)
|
|
33
|
+
4. `docs/plans/v0.8-implementation-plan.md` Batch 2 section (`### Batch 2 — Evidence indexing, canonical readers, and lazy data boundary`) plus cross-batch invariants (§7)
|
|
34
|
+
5. `docs/reports/v0.8-viewer-runtime-pwa-batch1.md` (§2 above)
|
|
35
|
+
6. `docs/ARCHITECTURE.md` (relevant sections, then edited)
|
|
36
|
+
7. `docs/CONTRACTS.md` (fully, in two paginated reads - artifact-kind/schema-version constants, directory/manifest-filename conventions, and the v0.7 external-reference media-ownership contract)
|
|
37
|
+
8. `docs/COMMANDS.md` (`view` section, then edited)
|
|
38
|
+
9. `docs/WORKFLOWS.md` (bounded-agent-context and external-reference workflow sections)
|
|
39
|
+
10. `package.json`, `src/viewerServer/httpServer.ts`, `src/viewerServer/viewerService.ts`, `src/viewerServer/port.ts`
|
|
40
|
+
11. `src/artifacts/artifactReader.ts`, `comparisonArtifactReader.ts`, `externalReferenceArtifactReader.ts`, `frontendContractArtifactReader.ts`, `frontendContractEvaluationArtifactReader.ts`, `types.ts`
|
|
41
|
+
12. `src/artifacts/externalReferenceArtifactWriter.ts` (directory/media-ownership shape)
|
|
42
|
+
13. `src/domain/schema.ts`, `comparison.ts`, `frontendContracts.ts`, `frontendContractEvaluationArtifact.ts`, `externalReference.ts`, `boundedAgentContext.ts`, `evidence.ts`, `completion.ts`
|
|
43
|
+
14. `tests/unit/cliFrontendContracts.test.ts` (fixture-construction pattern reused for Batch 2 test fixtures), `tests/unit/diagnostics.test.ts`, `tests/unit/externalReferenceImageFixtures.ts`
|
|
44
|
+
|
|
45
|
+
Confirmed the frozen plan is unchanged and Batch 2's title/scope match the task exactly.
|
|
46
|
+
|
|
47
|
+
## 4. my-dev-kit retrieval
|
|
48
|
+
|
|
49
|
+
Unlike Batch 1, this step was **run**, not skipped. Index built successfully:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
npx @dailephd/my-dev-kit@latest index --root . --src src --src tests --src viewer \
|
|
53
|
+
--out "<WORKFLOW_ROOT>\my-dev-kit-index" --call-graph --json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Result: `status: "complete"`, 73 files indexed. Ran all five required searches (reader ownership, artifact-write ownership, bounded-agent-context persistence, screenshot/media ownership, viewer server routes, and artifact-kind/schema-version constants). Every result matched direct source inspection exactly - in particular, the `BoundedAgentContextArtifact` search returned only `src/domain/boundedAgentContext.ts` and application-service files, **no** `boundedAgentContextArtifactReader.ts`/`Writer.ts` - corroborating (not merely asserting) that no disk reader/writer exists for that family. No `lookup`/`source`/`slice` follow-up was needed since search results were unambiguous. `MY_DEV_KIT_INDEX` = `Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-02\my-dev-kit-index`.
|
|
57
|
+
|
|
58
|
+
## 5. Evidence-family inventory
|
|
59
|
+
|
|
60
|
+
| Family | Kind constant | Schema | Persisted? | Writer | Reader (existing) | Discriminant | Media owned/referenced |
|
|
61
|
+
|---|---|---|---|---|---|---|---|
|
|
62
|
+
| Observation | `my-frontend-observer/observation` | `1.2.0` | Yes | `artifactWriter.ts` | `artifactReader.ts#readObservationArtifact` | - | owns `screenshot.png` (via `screenshot: EvidenceField<{path}>`) |
|
|
63
|
+
| Comparison | `my-frontend-observer/comparison` | `1.0.0` | Yes | `comparisonArtifactWriter.ts` | `comparisonArtifactReader.ts#readComparisonArtifact` | - | none (references `before`/`after` observation ids only) |
|
|
64
|
+
| Persistent baseline contract | `my-frontend-observer/frontend-contract` | `1.0.0` | Yes | `frontendContractArtifactWriter.ts#writePersistentBaselineContract` | `frontendContractArtifactReader.ts#readPersistentBaselineContract` | `contractClass: 'baseline'` | none |
|
|
65
|
+
| Per-change contract | `my-frontend-observer/frontend-contract` | `1.0.0` | Yes | `...#writePerChangeContract` | `...#readPerChangeContract` | `contractClass: 'change'` | none |
|
|
66
|
+
| Contract evaluation | `my-frontend-observer/frontend-contract-evaluation` | `1.0.0` | Yes | `frontendContractEvaluationArtifactWriter.ts` | `frontendContractEvaluationArtifactReader.ts` | - | none |
|
|
67
|
+
| External reference (imported) | `my-frontend-observer/external-reference` | `1.0.0` | Yes | `externalReferenceArtifactWriter.ts` | `externalReferenceArtifactReader.ts#readExternalReferenceArtifact` | `lifecycle.state: 'imported'` | owns `image.<ext>` (bare filename) |
|
|
68
|
+
| External reference (approved) | `my-frontend-observer/external-reference` | `1.0.0` | Yes | same | same | `lifecycle.state: 'approved'` | references, never copies, the imported artifact's image via `sourceReference.{referenceId, image}` |
|
|
69
|
+
| Bounded agent context | `my-frontend-observer/bounded-agent-context` | `1.0.0` | **No** | none | none | - | n/a |
|
|
70
|
+
|
|
71
|
+
Every family (all filename `manifest.json`, one artifact directory per instance) belongs in Batch 2 discovery **except** bounded-agent-context, which - per `docs/CONTRACTS.md` "v0.6 bounded agent context and correlation contract" ("there is no disk artifact writer/reader... it is a pure programmatic contract and derivation layer") and confirmed by my-dev-kit search (§4) - has no persisted filesystem representation to discover. Per task §8/§21, no persistence was invented for it; a manifest declaring that kind is classified `unrecognized-kind` (test: `evidenceDiscovery.test.ts` "a bounded-agent-context-kind manifest is treated as unrecognized").
|
|
72
|
+
|
|
73
|
+
## 6. Existing readers reused (no new readers were needed)
|
|
74
|
+
|
|
75
|
+
All five persisted families already had a canonical reader before this batch (`readObservationArtifact`, `readComparisonArtifact`, `readPersistentBaselineContract`, `readPerChangeContract`, `readFrontendContractEvaluationArtifact`, `readExternalReferenceArtifact`) - confirmed by direct inspection (§3) and my-dev-kit search (§4). **No new reader was added.** `src/viewerServer/evidence/classify.ts` calls these six functions unchanged; it never re-implements JSON parsing or structural validation.
|
|
76
|
+
|
|
77
|
+
## 7. Canonical validators reused
|
|
78
|
+
|
|
79
|
+
Every classification decision for a "supported" or "invalid-structure" result comes from the family's own existing validator, invoked transitively by its reader (`isValidObservationArtifact`, `isValidComparisonArtifact`, `isValidPersistentBaselineContract`, `isValidPerChangeContract`, `isValidFrontendContractEvaluationArtifact`, `isValidExternalReferenceArtifact`). No second/parallel validator was introduced anywhere in `src/viewerServer/evidence/`. For the shared `frontend-contract` kind, `classify.ts` tries `readPersistentBaselineContract` then `readPerChangeContract` in sequence - the **existing validators**, not classifier-owned logic, decide which (if either) a candidate actually is.
|
|
80
|
+
|
|
81
|
+
## 8. Index bounds selected (and rationale)
|
|
82
|
+
|
|
83
|
+
Defined in `src/viewerServer/evidence/limits.ts`, chosen after inspecting that every current writer produces a shallow `<outputLocation>/<id>/manifest.json` shape (§5) rather than a deep tree:
|
|
84
|
+
|
|
85
|
+
- `MAX_DISCOVERY_DEPTH = 6` (typical real shape needs depth 2; generous slack for a nested `--output`, still bounded);
|
|
86
|
+
- `MAX_DIRECTORIES_VISITED = 2000`;
|
|
87
|
+
- `MAX_MANIFEST_CANDIDATES = 1000` (candidate `manifest.json` files classified);
|
|
88
|
+
- `MAX_INDEX_RECORDS = 500` (metadata records returned in one `/api/index` response);
|
|
89
|
+
- `MAX_MANIFEST_CANDIDATE_BYTES = 2,000,000` - a candidate exceeding this is never read into memory; classified `unreadable` instead. Real manifests are small (identity/geometry/config JSON only; screenshots/images are always separate sibling files - §5), so this is generous headroom, not a tight fit.
|
|
90
|
+
|
|
91
|
+
`discoverManifests` opens **only** files literally named `manifest.json` - no other filename is ever read or classified, regardless of content (even valid JSON), satisfying "do not treat every `manifest.json` as valid Observer evidence... do not treat arbitrary JSON files as viewer artifacts" without needing a broader allow/deny list.
|
|
92
|
+
|
|
93
|
+
## 9. Symlink/junction policy
|
|
94
|
+
|
|
95
|
+
`discovery.ts#walk` checks `dirent.isSymbolicLink()` on every entry (file or directory) via `readdir(dir, {withFileTypes:true})` and skips it entirely - never followed, never opened. Verified with a real symlinked directory pointing outside the evidence root (`evidenceDiscovery.test.ts` "never follows a directory symlink out of the evidence root"; the test degrades gracefully rather than failing if symlink creation requires elevated privileges in a given Windows configuration - confirmed working in this environment). Since symlinks are never followed, no separate `realpath`-based re-containment check was needed for the walk itself; `resolveContainedDir`/`resolveContainedFile` (§13) provide that containment guarantee independently for handle-driven (not walk-driven) access.
|
|
96
|
+
|
|
97
|
+
## 10. Viewer handle design
|
|
98
|
+
|
|
99
|
+
A handle is `<family-slug>:<percent-encoded root-relative directory path>` (`evidence/handles.ts`) - e.g. `observation:observations%2Fsmoke-obs-before`. It is "server-issued" only in the sense that it is exactly what discovery/classification already produced; it grants no filesystem access by itself. **Every** route that accepts a handle (`/api/artifacts/<handle>`, `/api/media/<handle>/<role>`) independently decodes it, re-resolves it against the evidence root via `resolveContainedDir` (re-checking containment), and re-classifies that one candidate through the same canonical-reader dispatch the index uses - it never trusts a cached record or a client-supplied path string. A handle whose backing directory no longer exists, or whose re-classified family no longer matches what the handle claims, fails closed as "unknown handle" (404), never served as stale.
|
|
100
|
+
|
|
101
|
+
## 11. API routes and semantics
|
|
102
|
+
|
|
103
|
+
| Route | Method | Semantics |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `GET /api/status` | GET/HEAD | Unchanged from Batch 1. |
|
|
106
|
+
| `GET /api/index` | GET/HEAD | Bounded, metadata-only, deterministic (rebuilt fresh every call - no persistent cache). |
|
|
107
|
+
| `GET /api/artifacts/<handle>` | GET/HEAD | Known handle only; one canonical-reader read; on demand; no schema upgrade; no mutation. `200` supported / `404` unknown handle / `409` handle resolves but is not currently loadable (unsupported-version, invalid-structure, malformed-json, unreadable - carries the same honest `metadata` the index would show). |
|
|
108
|
+
| `GET /api/media/<handle>/<role>` | GET/HEAD | Known handle + known role only; root/artifact-contained; on demand; streamed. `200` / `404` for any failure (unknown handle, unknown/inapplicable role, missing file). |
|
|
109
|
+
|
|
110
|
+
All four reject every write method (`POST`/`PUT`/`PATCH`/`DELETE`) with `405 Method Not Allowed` (enforced once, ahead of all routing, in `handleRequest` - unchanged Batch 1 mechanism now covers the new routes too; verified in `viewerEvidenceServer.test.ts`).
|
|
111
|
+
|
|
112
|
+
## 12. Viewer adapter architecture
|
|
113
|
+
|
|
114
|
+
`src/viewerServer/evidence/projection.ts`:
|
|
115
|
+
|
|
116
|
+
- `EvidenceMetadataRecord` - the `/api/index` shape: `handle`, `family`, `supportState`, `relativeDir`, and (only when meaningful for that family/state) `logicalId`, `schemaVersion`, `foundSchemaVersion`, `producerVersion`, `completion`, `lifecycleState`, `contractClass`, `overallVerdict`, `comparability`, `media` (availability summary only, never bytes), `relatedIds`, `message`. Verified to exclude full-payload fields (`targetEvidence`, `pageEvidence`, `requestConfig`) via a direct serialization assertion.
|
|
117
|
+
- `EvidenceArtifactDetail` - the `/api/artifacts/<handle>` shape: the already-validated domain object wrapped with `handle`/`family`. Selects/wraps existing canonical fields only; derives nothing new, recomputes nothing, persists nothing (no `viewer.json`/viewer-manifest/cache artifact of any kind exists anywhere in this batch).
|
|
118
|
+
|
|
119
|
+
Both are ephemeral: constructed fresh per request, held only in memory for the duration of that request/response.
|
|
120
|
+
|
|
121
|
+
## 13. Absolute-path exposure policy
|
|
122
|
+
|
|
123
|
+
Metadata records expose only a root-relative `relativeDir` (POSIX-normalized), never an absolute filesystem path. `/api/status`'s pre-existing `root` field (Batch 1, unchanged) remains the only place an absolute path is echoed back, and only as an opaque display string the server itself supplied - never accepted as input from the browser.
|
|
124
|
+
|
|
125
|
+
## 14. Media ownership/resolution behavior
|
|
126
|
+
|
|
127
|
+
`src/viewerServer/evidence/mediaResolver.ts` recognizes exactly three roles: `screenshot` (observation only), `image` (imported external reference only), `source-image` (approved external reference only) - any other role, or a role requested against a family it doesn't apply to, is rejected (`404`). Every resolved path goes through `resolveContainedFile`, which additionally rejects any filename containing a path separator outright (every real media reference is contractually a bare filename - `docs/CONTRACTS.md` "v0.7 Prompt 1" `ExternalReferenceImageReference.path`).
|
|
128
|
+
|
|
129
|
+
## 15. Approved external-reference source-image behavior
|
|
130
|
+
|
|
131
|
+
Confirmed via `docs/CONTRACTS.md`/`externalReference.ts` (§3/§5) and proven by a real fixture (`writeExternalReferencePairFixture`, using the actual `importExternalReference`/`approveExternalReference` application services): an approved artifact's directory contains **only** `manifest.json` - no image file. `source-image` resolution reads the approved artifact's `sourceReference.referenceId`, then calls `evidence/index.ts#findImportedReferenceDir` - a bounded search (reusing the same discovery+classify pipeline, capped at `MAX_INDEX_RECORDS`) for the **imported** artifact whose own `referenceId` matches - and only then resolves `sourceReference.image.path` against *that* artifact's real directory. Never assumes co-location. Two tests prove this exactly: "resolves an approved reference's source-image through the owning imported artifact, never assuming co-location" (success case, real cross-artifact resolution) and "an approved reference whose owning imported artifact is absent from the root reports missing media honestly" (the imported artifact deliberately left out of the root - `404`, not a crash or a fabricated image). The full built-CLI smoke test (§20) additionally proves this against the real compiled server.
|
|
132
|
+
|
|
133
|
+
## 16. Unsupported-version behavior
|
|
134
|
+
|
|
135
|
+
`classify.ts` compares the candidate's `schemaVersion` against the current canonical constant (`SCHEMA_VERSION`, `COMPARISON_SCHEMA_VERSION`, `CONTRACT_SCHEMA_VERSION`, `EVALUATION_SCHEMA_VERSION`, `EXTERNAL_REFERENCE_SCHEMA_VERSION`) **before** attempting a canonical read, so an unsupported version never even reaches (and is never silently accepted by) the reader/validator pair pinned to the current schema. `/api/index` reports `supportState: 'unsupported-version'` plus both the found and supported version strings; `/api/artifacts/<handle>` refuses to load it (`409`, carrying that same honest metadata) - never coerced into a fabricated current-shape domain object. Proven with a real fixture (`writeUnsupportedVersionManifest`: a genuine `my-frontend-observer/observation` kind at schema `99.0.0`) at the unit level and in the real-Chromium/CLI smoke.
|
|
136
|
+
|
|
137
|
+
## 17. Malformed/unavailable/missing behavior
|
|
138
|
+
|
|
139
|
+
Six independent, honestly distinguished states (`ViewerSupportState` in `classify.ts`): `supported`, `unsupported-version`, `invalid-structure` (kind+version match but the canonical validator itself rejects it), `unrecognized-kind` (no known `artifactKind`, including the bounded-agent-context case), `malformed-json` (not valid JSON at all), `unreadable` (stat/read failure or over the size bound). None is ever silently dropped or reinterpreted as `supported`; one malformed/unrelated candidate never erases valid neighboring evidence (`evidenceDiscovery.test.ts`: 6 real pipeline artifacts + 1 malformed all appear in one index response). Missing media is reported per-role (`available: false` with an honest `reason`) rather than as a whole-artifact failure - a genuinely deleted `screenshot.png` still yields a `supported` observation record whose `media` summary honestly says `unavailable`.
|
|
140
|
+
|
|
141
|
+
## 18. PWA cache verification
|
|
142
|
+
|
|
143
|
+
No `vite.config.ts`/service-worker configuration change was needed: every new route lives under `/api/`, already covered by Batch 1's `navigateFallbackDenylist: [/^\/api\//]`. Extended `tests/unit/viewerPwaBuild.test.ts` with an explicit Batch 2 assertion against the **real built** `sw.js`: still exactly one `registerRoute` call after the server-side additions, and the precache manifest contains no `/api/index`, `/api/artifacts`, or `/api/media` entry.
|
|
144
|
+
|
|
145
|
+
## 19. Batch 2 UI changes
|
|
146
|
+
|
|
147
|
+
`viewer/src/hooks/useEvidenceIndex.ts` (fetches `/api/index`), `useArtifactDetail.ts` (fetches `/api/artifacts/<handle>` only when a handle is selected - never eagerly), `components/EvidenceList.tsx` (bounded list, one item per record, honest support-state label per item), `components/ArtifactPreview.tsx` (loading/loaded/error/unsupported states; a bounded raw-JSON preview of the loaded artifact - explicitly *not* a real visualization). `App.tsx` wires these into the existing Batch 1 shell regions (nav = list, workspace = preview, details aside = selected record's own already-fetched metadata fields - no additional request). No screenshot rendering, SVG overlay, or comparison/contract/reference visualization exists anywhere in `viewer/src/` (verified by a real-Chromium test asserting zero `<img>`/`<svg>` elements after loading a real observation).
|
|
148
|
+
|
|
149
|
+
## 20. Files created
|
|
150
|
+
|
|
151
|
+
- `src/viewerServer/evidence/limits.ts`, `discovery.ts`, `classify.ts`, `handles.ts`, `pathSafety.ts`, `projection.ts`, `index.ts`, `mediaResolver.ts`
|
|
152
|
+
- `tests/support/evidenceFixtures.ts` (shared real-evidence fixture builders, reusing the existing writer/application-service functions - mirrors `tests/unit/cliFrontendContracts.test.ts`'s established construction pattern)
|
|
153
|
+
- `tests/unit/evidenceDiscovery.test.ts`, `viewerEvidenceServer.test.ts`
|
|
154
|
+
- `tests/browser/viewerEvidenceShell.test.ts`
|
|
155
|
+
- `viewer/src/hooks/useEvidenceIndex.ts`, `useArtifactDetail.ts`
|
|
156
|
+
- `viewer/src/components/EvidenceList.tsx`, `ArtifactPreview.tsx`
|
|
157
|
+
- `docs/reports/v0.8-evidence-index-readers-batch2.md` (this file)
|
|
158
|
+
|
|
159
|
+
## 21. Files modified
|
|
160
|
+
|
|
161
|
+
- `src/viewerServer/httpServer.ts` - added `/api/index`, `/api/artifacts/<handle>`, `/api/media/<handle>/<role>` routing plus shared `writeJsonBody`/`writeJsonError`/`streamFile`/`decodeURIComponentSafe` helpers; existing `/api/status` and static-asset serving unchanged.
|
|
162
|
+
- `viewer/src/App.tsx` - wires the new hooks/components into the existing shell regions.
|
|
163
|
+
- `viewer/src/styles/index.css` - additive rules for the evidence list/preview/details UI.
|
|
164
|
+
- `tests/browser/viewerShell.test.ts` - one pre-existing Batch 1 assertion (`toContain('Evidence navigation will appear here')`, a *static placeholder* string) legitimately no longer holds now that the nav region is live; updated to assert the new, honest "no recognized evidence" message for an empty root - the same kind of sanctioned evolution as Batch 1's own `TST-401` update.
|
|
165
|
+
- `tests/unit/viewerPwaBuild.test.ts` - added the explicit Batch 2 cache-boundary assertion (§18).
|
|
166
|
+
- `docs/ARCHITECTURE.md` - new "v0.8 Batch 2" section.
|
|
167
|
+
- `docs/COMMANDS.md` - `view` section updated to describe the new routes/behavior.
|
|
168
|
+
|
|
169
|
+
## 22. A production bug found and fixed by real-CLI smoke testing
|
|
170
|
+
|
|
171
|
+
`src/viewerServer/evidence/pathSafety.ts#resolveContainedDir` originally compared a `path.resolve()`-normalized candidate path against the **un-normalized** `root` string. Every unit test's `root` came from `path.join(tmpdir(), ...)`, which Node already normalizes to the platform separator, masking the bug entirely - all 19 initial `viewerEvidenceServer.test.ts` tests passed. The real built-CLI smoke test (§23), run with `--root` supplied using forward slashes (as a user typing a path in a shell commonly would, even on Windows), reproduced it immediately: every artifact/media request returned `404 unknown handle` even for evidence that genuinely existed. Root-caused via direct `node -e` reproduction against the compiled `dist` output, fixed by resolving `root` itself before the prefix comparison, and covered by a new dedicated regression test (`viewerEvidenceServer.test.ts` "root path normalization" describe block) that deliberately constructs a forward-slash root and asserts a real handle still resolves. This is exactly the kind of defect the task's mandated built-CLI smoke step (§34) exists to catch, and it did.
|
|
172
|
+
|
|
173
|
+
## 23. Validation results
|
|
174
|
+
|
|
175
|
+
| Command | Result |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `npm run typecheck` | **PASS** (zero errors, both `tsconfig.json` and `viewer/tsconfig.json`) |
|
|
178
|
+
| `npm run lint` | **PASS** (zero errors/warnings) |
|
|
179
|
+
| `npm test` | **PASS** - 1071/1071 tests, 56/56 files |
|
|
180
|
+
| `npm run build` | **PASS** - unchanged Node/library output plus `dist/viewerServer/evidence/**` and the rebuilt `dist/viewer/**` PWA |
|
|
181
|
+
| `npm run check:docs` | **PASS** - "Documentation check passed (17 required files)." |
|
|
182
|
+
| `npm run test:browser` | **PASS** - 132/132 tests, 12/12 files (real Chromium; run in full, not skipped, per task §33's instruction since this batch directly changes the browser-facing data boundary) |
|
|
183
|
+
| `git diff --check` | **PASS** - no whitespace errors (only expected LF→CRLF line-ending notices) |
|
|
184
|
+
|
|
185
|
+
## 24. Built viewer Batch 2 smoke (§34)
|
|
186
|
+
|
|
187
|
+
Fixture: a real evidence root built via a one-off script (not committed; lives under `WORKFLOW_ROOT\tmp`) that calls the actual compiled `dist/artifacts/*Writer.js` and `dist/application/*Service.js` modules directly - the same real writers/services the CLI itself uses - to produce two real observations, a real comparison, a real baseline contract, a real per-change contract, a real evaluation, a real imported external reference (with a real image file), and a real approved external reference, plus a malformed-JSON candidate, an unsupported-future-schema-version candidate, and one unrelated `README.txt`, all under `WORKFLOW_ROOT\smoke\evidence-root`.
|
|
188
|
+
|
|
189
|
+
Command: `node dist/cli.js view --root "<WORKFLOW_ROOT>\smoke\evidence-root" --port 4319 --no-open`
|
|
190
|
+
|
|
191
|
+
All required checks (§34), against the real running built server:
|
|
192
|
+
|
|
193
|
+
- `/api/status` → `200`, correct root echoed.
|
|
194
|
+
- `/api/index` → `200`, all 9 real evidence records present with correct `family`/`supportState`, plus the malformed and unsupported-version candidates shown honestly; response body contains no full-artifact fields (`targetEvidence` etc.) and no `README` reference.
|
|
195
|
+
- Selected supported artifact (`observation:observations%2Fsmoke-obs-before`) loaded on demand via `/api/artifacts/<handle>` → `200` with its full payload.
|
|
196
|
+
- Selected media loaded on demand: observation `screenshot` → `200 image/png`; imported reference `image` → `200 image/png`; **approved reference `source-image` → `200 image/png`, resolved through the separate imported artifact's directory** (§15/§22).
|
|
197
|
+
- Unsupported-version and malformed candidates both correctly refused at `/api/artifacts/<handle>` → `409` (not fabricated as loaded).
|
|
198
|
+
- Unrelated `README.txt` never appears in `/api/index` and is not directly servable (`GET /README.txt` → `404`).
|
|
199
|
+
- Path-traversal attempt (`/api/artifacts/observation:..%2F..%2Fetc`) → `404`.
|
|
200
|
+
- Absolute-path-shaped handle attempt → `404`.
|
|
201
|
+
- PWA still loads: `/` → `200`, `/manifest.webmanifest` → `200`, `/sw.js` → `200`.
|
|
202
|
+
- `netstat` confirmed the listener bound to `127.0.0.1:4319` only (never `0.0.0.0`).
|
|
203
|
+
- Server located by its real PID via `netstat` and terminated with `taskkill /F`; a follow-up `netstat` confirmed the port was released and no server process remained.
|
|
204
|
+
- A post-run directory listing of the smoke evidence root shows exactly the fixture's own files - no stray `.tmp-*` write-in-progress directories, no modified/deleted files.
|
|
205
|
+
|
|
206
|
+
**Result: PASS.** Logs retained under `WORKFLOW_ROOT\logs\` (`smoke-server.log`, `smoke-server-2.log` [post-fix rerun], `index-response.json`, `index-response-2.json`, `smoke-checks-final.log`).
|
|
207
|
+
|
|
208
|
+
## 25. Existing command regression check
|
|
209
|
+
|
|
210
|
+
`observe`, `compare`, `approve-baseline`, `save-change-contract`, `evaluate-contract`, `import-reference`, `approve-reference`, `evaluate-reference-fidelity`, Batch 1 `view` startup, PWA shell, fixed host/port behavior, and the PWA cache boundary all remain covered by their existing, unmodified test suites (all 1071 unit + 132 browser tests pass - §23). Canonical v0.1-v0.7 semantics were not touched anywhere in this batch's diff; `src/domain/`, `src/artifacts/*Writer.ts`, and every existing reader are byte-for-byte unchanged.
|
|
211
|
+
|
|
212
|
+
## 26. Generated path inventory
|
|
213
|
+
|
|
214
|
+
| Path | Disposition |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `WORKFLOW_ROOT\cache\npm` | Retained (npm cache from the my-dev-kit-index `npx` invocation; harmless, reusable) |
|
|
217
|
+
| `WORKFLOW_ROOT\tmp\vite-cache`, `WORKFLOW_ROOT\tmp\build-smoke-evidence.mjs` | Retained (build cache dir was empty again, same as Batch 1 - Vite build mode doesn't populate it; the smoke-fixture script is dev/readiness tooling only, not committed) |
|
|
218
|
+
| `WORKFLOW_ROOT\logs\*` | Retained (smoke evidence - §24) |
|
|
219
|
+
| `WORKFLOW_ROOT\smoke\evidence-root` | Retained (real evidence fixture tree built for the smoke test - §24) |
|
|
220
|
+
| `WORKFLOW_ROOT\my-dev-kit-index\*` | Retained (successful index + cache-metadata from §4) |
|
|
221
|
+
| `WORKFLOW_ROOT\{fixtures,candidate,pack}` | Retained, empty/unused this batch (no `npm pack`/candidate-install step was needed since no packaging-boundary change occurred beyond the existing `dist` allowlist) |
|
|
222
|
+
| Repo-root `dist/` | Ordinary build output (gitignored); rebuilt cleanly by `scripts/clean.mjs` on every `npm run build` |
|
|
223
|
+
| Pre-existing repo-root `.my-dev-kit*`/`baselines`/`comparisons`/`contracts`/`evaluations`/`observations` | Pre-existing, empty, untouched (same finding as the Batch 1 report) |
|
|
224
|
+
|
|
225
|
+
`Z:\Users\newuser\Projects\my-frontend-observer.my-dev-kit-workflow\v0.8\batch-01` was never targeted by any command in this session (verified) - preserved exactly as Batch 1 left it.
|
|
226
|
+
|
|
227
|
+
## 27. Repository pollution check
|
|
228
|
+
|
|
229
|
+
`git status --short` before staging showed only the seven modified + eight new Batch-2-owned paths listed in §20/§21 (plus their containing directories). No unexpected file or directory appeared anywhere in the repository as a result of this batch's work.
|
|
230
|
+
|
|
231
|
+
## 28. Skipped work / deviations
|
|
232
|
+
|
|
233
|
+
- None. Every task step (my-dev-kit retrieval, all six required test categories, the full validation chain including `test:browser`, and the built-CLI smoke) was executed, not skipped or substituted.
|
|
234
|
+
|
|
235
|
+
## 29. Remaining uncovered risks
|
|
236
|
+
|
|
237
|
+
- **`findImportedReferenceDir` re-walks the evidence tree per `source-image` request** rather than reusing a cached index (by design - no persistent cache exists, per task §30). For an evidence root at the bound edge (near `MAX_MANIFEST_CANDIDATES`), an approved-reference-heavy workload would re-pay that walk cost on every source-image request. Not a correctness risk; a potential future performance consideration only if real usage ever approaches the bound (task §30 explicitly says to report rather than pre-optimize).
|
|
238
|
+
- **The bounded-agent-context "unrecognized-kind" classification is asserted only at the unit level**, not against a real persisted fixture (none exists to build, by design - §5). The behavior is exercised with a hand-written manifest declaring that kind, which is the most realistic proof achievable without inventing persistence for a family that has none.
|
|
239
|
+
- Batch 1's previously reported risks (install-prompt "available" branch, live service-worker execution) remain unresolved and out of this batch's scope.
|
|
240
|
+
|
|
241
|
+
## 30. Out-of-scope confirmation
|
|
242
|
+
|
|
243
|
+
Confirmed absent from this batch's diff: target SVG overlays, screenshot geometry rendering, runtime target inspection UI, relationship/scroll visualization, before/after visual comparison, contract/change-scope visualization, external-reference/candidate side-by-side visual inspection, region/target overlays, explicit-binding cross-selection, zoom/pan, synchronized lock, on-demand fidelity computation, bounded-agent-context visual inspection, correlation UI, arbitrary filesystem navigation, annotation, region/requirement authoring, source editing, automatic binding/target discovery, pixel-diff scoring, computer vision, cloud hosting, database, authentication, collaboration. `ArtifactPreview.tsx`'s raw-JSON preview is explicitly not a visualization (no image, no SVG, no geometry rendering - proven by the zero-`<img>`/zero-`<svg>` browser assertion, §19).
|
|
244
|
+
|
|
245
|
+
## 31. Final verdict
|
|
246
|
+
|
|
247
|
+
Batch 2 ("Evidence indexing, canonical readers, and lazy data boundary") is implemented and independently validated: a built candidate's viewer can enumerate real recognized evidence from bounded metadata, load one selected artifact on demand through its existing canonical validation boundary, load its owned/referenced media safely (including the cross-artifact approved-reference case), and visibly distinguish malformed/unsupported/missing evidence rather than silently dropping it - proven at the unit, real-Chromium, and real-built-CLI-smoke levels, with one genuine cross-platform path-handling defect found and fixed along the way. No Batch 3+ visualization, no v0.9 annotation, and no release/publication action was taken. Package version remains `0.7.0`.
|