my-frontend-observer 0.1.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +69 -0
  3. package/dist/application/browserCaptureService.d.ts +11 -0
  4. package/dist/application/browserCaptureService.js +12 -0
  5. package/dist/application/browserCaptureService.js.map +1 -0
  6. package/dist/application/observationPersistence.d.ts +59 -0
  7. package/dist/application/observationPersistence.js +78 -0
  8. package/dist/application/observationPersistence.js.map +1 -0
  9. package/dist/artifacts/artifactWriter.d.ts +25 -0
  10. package/dist/artifacts/artifactWriter.js +68 -0
  11. package/dist/artifacts/artifactWriter.js.map +1 -0
  12. package/dist/artifacts/types.d.ts +17 -0
  13. package/dist/artifacts/types.js +2 -0
  14. package/dist/artifacts/types.js.map +1 -0
  15. package/dist/browser/chromiumAdapter.d.ts +21 -0
  16. package/dist/browser/chromiumAdapter.js +150 -0
  17. package/dist/browser/chromiumAdapter.js.map +1 -0
  18. package/dist/browser/evidenceCapture.d.ts +19 -0
  19. package/dist/browser/evidenceCapture.js +201 -0
  20. package/dist/browser/evidenceCapture.js.map +1 -0
  21. package/dist/browser/types.d.ts +22 -0
  22. package/dist/browser/types.js +2 -0
  23. package/dist/browser/types.js.map +1 -0
  24. package/dist/cli.d.ts +7 -0
  25. package/dist/cli.js +216 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/domain/completion.d.ts +30 -0
  28. package/dist/domain/completion.js +22 -0
  29. package/dist/domain/completion.js.map +1 -0
  30. package/dist/domain/diagnostics.d.ts +17 -0
  31. package/dist/domain/diagnostics.js +55 -0
  32. package/dist/domain/diagnostics.js.map +1 -0
  33. package/dist/domain/evidence.d.ts +27 -0
  34. package/dist/domain/evidence.js +55 -0
  35. package/dist/domain/evidence.js.map +1 -0
  36. package/dist/domain/identity.d.ts +13 -0
  37. package/dist/domain/identity.js +37 -0
  38. package/dist/domain/identity.js.map +1 -0
  39. package/dist/domain/schema.d.ts +111 -0
  40. package/dist/domain/schema.js +126 -0
  41. package/dist/domain/schema.js.map +1 -0
  42. package/dist/index.d.ts +21 -0
  43. package/dist/index.js +12 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/request/paths.d.ts +14 -0
  46. package/dist/request/paths.js +33 -0
  47. package/dist/request/paths.js.map +1 -0
  48. package/dist/request/request.d.ts +43 -0
  49. package/dist/request/request.js +174 -0
  50. package/dist/request/request.js.map +1 -0
  51. package/dist/safety/policy.d.ts +14 -0
  52. package/dist/safety/policy.js +81 -0
  53. package/dist/safety/policy.js.map +1 -0
  54. package/docs/ARCHITECTURE.md +85 -0
  55. package/docs/CI_CD.md +28 -0
  56. package/docs/COMMANDS.md +82 -0
  57. package/docs/CONTRACTS.md +54 -0
  58. package/docs/CURRENT_STATE.md +113 -0
  59. package/docs/DEVELOPMENT.md +65 -0
  60. package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +33 -0
  61. package/docs/PROJECT_DESCRIPTION.md +1770 -0
  62. package/docs/PROJECT_MILESTONES.md +2073 -0
  63. package/docs/PROJECT_OVERVIEW.md +53 -0
  64. package/docs/QUICKSTART.md +35 -0
  65. package/docs/RELEASE.md +9 -0
  66. package/docs/ROADMAP.md +352 -0
  67. package/docs/SECURITY.md +33 -0
  68. package/docs/WORKFLOWS.md +51 -0
  69. package/package.json +46 -0
@@ -0,0 +1,174 @@
1
+ import { orderDiagnostics } from '../domain/diagnostics.js';
2
+ import { classifyUrl } from '../safety/policy.js';
3
+ import { normalizeOutputLocation } from './paths.js';
4
+ const DEFAULT_VIEWPORT = { width: 1280, height: 720 };
5
+ const DEFAULT_TIMEOUT_MS = 30000;
6
+ const DEFAULT_READINESS_CONDITION = 'load';
7
+ const DEFAULT_READINESS_TIMEOUT_MS = 10000;
8
+ const DEFAULT_OUTPUT_LOCATION = 'observations';
9
+ const VIEWPORT_MIN = 200;
10
+ const VIEWPORT_MAX = 3840;
11
+ const TIMEOUT_MIN_MS = 1000;
12
+ const TIMEOUT_MAX_MS = 120000;
13
+ const READINESS_TIMEOUT_MIN_MS = 500;
14
+ const MAX_TARGETS = 20;
15
+ const TARGET_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
16
+ const MAX_SELECTOR_LENGTH = 500;
17
+ function isPlainObject(value) {
18
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
19
+ }
20
+ function isInRange(value, min, max) {
21
+ return Number.isInteger(value) && value >= min && value <= max;
22
+ }
23
+ /**
24
+ * Total, never throws. Collects every applicable diagnostic before returning
25
+ * (does not short-circuit on the first violation) so callers see every
26
+ * problem at once, in deterministic order.
27
+ */
28
+ export function normalizeRequest(raw) {
29
+ const diagnostics = [];
30
+ let targetUrl;
31
+ if (typeof raw.targetUrl !== 'string' || raw.targetUrl.length === 0) {
32
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'targetUrl is required and must be a non-empty string' });
33
+ }
34
+ else {
35
+ targetUrl = raw.targetUrl;
36
+ const decision = classifyUrl(targetUrl);
37
+ if (!decision.allowed)
38
+ diagnostics.push(decision.diagnostic);
39
+ }
40
+ let viewport = DEFAULT_VIEWPORT;
41
+ if (raw.viewport !== undefined) {
42
+ const candidate = raw.viewport;
43
+ if (!isPlainObject(candidate) ||
44
+ typeof candidate.width !== 'number' ||
45
+ typeof candidate.height !== 'number' ||
46
+ !isInRange(candidate.width, VIEWPORT_MIN, VIEWPORT_MAX) ||
47
+ !isInRange(candidate.height, VIEWPORT_MIN, VIEWPORT_MAX)) {
48
+ diagnostics.push({
49
+ code: 'invalid-request',
50
+ severity: 'error',
51
+ message: `viewport width/height must be integers in [${VIEWPORT_MIN}, ${VIEWPORT_MAX}]`,
52
+ });
53
+ }
54
+ else {
55
+ viewport = { width: candidate.width, height: candidate.height };
56
+ }
57
+ }
58
+ const targets = [];
59
+ if (raw.targets !== undefined) {
60
+ if (!Array.isArray(raw.targets)) {
61
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'targets must be an array' });
62
+ }
63
+ else if (raw.targets.length > MAX_TARGETS) {
64
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: `targets must contain at most ${MAX_TARGETS} entries` });
65
+ }
66
+ else {
67
+ const seenNames = new Set();
68
+ for (const rawTarget of raw.targets) {
69
+ if (!isPlainObject(rawTarget) || typeof rawTarget.name !== 'string' || typeof rawTarget.selector !== 'string') {
70
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'each target requires a string name and string selector' });
71
+ continue;
72
+ }
73
+ const name = rawTarget.name;
74
+ const selector = rawTarget.selector;
75
+ if (!TARGET_NAME_RE.test(name)) {
76
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'target name must match ^[A-Za-z0-9_-]{1,64}$', targetName: name });
77
+ }
78
+ if (selector.length === 0 || selector.length > MAX_SELECTOR_LENGTH) {
79
+ diagnostics.push({
80
+ code: 'invalid-request',
81
+ severity: 'error',
82
+ message: `target selector must be 1-${MAX_SELECTOR_LENGTH} characters`,
83
+ targetName: name,
84
+ });
85
+ }
86
+ const normalizedName = name.toLowerCase();
87
+ if (seenNames.has(normalizedName)) {
88
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'duplicate target name', targetName: name });
89
+ }
90
+ else {
91
+ seenNames.add(normalizedName);
92
+ }
93
+ targets.push({ name, selector });
94
+ }
95
+ }
96
+ }
97
+ let timeoutMs = DEFAULT_TIMEOUT_MS;
98
+ if (raw.timeoutMs !== undefined) {
99
+ if (typeof raw.timeoutMs !== 'number' || !isInRange(raw.timeoutMs, TIMEOUT_MIN_MS, TIMEOUT_MAX_MS)) {
100
+ diagnostics.push({
101
+ code: 'invalid-request',
102
+ severity: 'error',
103
+ message: `timeoutMs must be an integer in [${TIMEOUT_MIN_MS}, ${TIMEOUT_MAX_MS}]`,
104
+ });
105
+ }
106
+ else {
107
+ timeoutMs = raw.timeoutMs;
108
+ }
109
+ }
110
+ let readinessCondition = DEFAULT_READINESS_CONDITION;
111
+ let readinessTimeoutMs = Math.min(DEFAULT_READINESS_TIMEOUT_MS, timeoutMs);
112
+ if (raw.readiness !== undefined) {
113
+ if (!isPlainObject(raw.readiness)) {
114
+ diagnostics.push({ code: 'unsupported-configuration', severity: 'error', message: 'readiness must be an object' });
115
+ }
116
+ else {
117
+ const readinessRaw = raw.readiness;
118
+ if (readinessRaw.condition !== undefined) {
119
+ if (readinessRaw.condition === 'load' || readinessRaw.condition === 'domcontentloaded') {
120
+ readinessCondition = readinessRaw.condition;
121
+ }
122
+ else {
123
+ diagnostics.push({
124
+ code: 'unsupported-configuration',
125
+ severity: 'error',
126
+ message: 'readiness.condition must be "load" or "domcontentloaded"',
127
+ });
128
+ }
129
+ }
130
+ if (readinessRaw.timeoutMs !== undefined) {
131
+ if (typeof readinessRaw.timeoutMs !== 'number' || !isInRange(readinessRaw.timeoutMs, READINESS_TIMEOUT_MIN_MS, timeoutMs)) {
132
+ diagnostics.push({
133
+ code: 'unsupported-configuration',
134
+ severity: 'error',
135
+ message: `readiness.timeoutMs must be an integer in [${READINESS_TIMEOUT_MIN_MS}, timeoutMs]`,
136
+ });
137
+ }
138
+ else {
139
+ readinessTimeoutMs = readinessRaw.timeoutMs;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ let outputLocation = DEFAULT_OUTPUT_LOCATION;
145
+ if (raw.outputLocation !== undefined) {
146
+ if (typeof raw.outputLocation !== 'string') {
147
+ diagnostics.push({ code: 'invalid-request', severity: 'error', message: 'outputLocation must be a string' });
148
+ }
149
+ else {
150
+ const result = normalizeOutputLocation(raw.outputLocation);
151
+ if (result.ok) {
152
+ outputLocation = result.value;
153
+ }
154
+ else {
155
+ diagnostics.push(result.diagnostic);
156
+ }
157
+ }
158
+ }
159
+ if (diagnostics.length > 0 || targetUrl === undefined) {
160
+ return { ok: false, diagnostics: orderDiagnostics(diagnostics) };
161
+ }
162
+ return {
163
+ ok: true,
164
+ request: {
165
+ targetUrl,
166
+ viewport,
167
+ targets,
168
+ outputLocation,
169
+ timeoutMs,
170
+ readiness: { condition: readinessCondition, timeoutMs: readinessTimeoutMs },
171
+ },
172
+ };
173
+ }
174
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/request/request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Diagnostic } from '../domain/diagnostics.js';
2
+ export type SafetyDecision = {
3
+ allowed: true;
4
+ } | {
5
+ allowed: false;
6
+ diagnostic: Diagnostic;
7
+ };
8
+ /** Pure classification: scheme + loopback rules only. Never performs DNS resolution or network I/O. */
9
+ export declare function classifyUrl(url: string): SafetyDecision;
10
+ export declare function classifyRedirect(fromUrl: string, toUrl: string): SafetyDecision;
11
+ export declare function classifySubresource(url: string): SafetyDecision;
12
+ /** Popups and downloads are never followed/saved in v0.1; both are non-fatal (warning) notices, not errors. */
13
+ export declare function classifyPopup(): Diagnostic;
14
+ export declare function classifyDownload(): Diagnostic;
@@ -0,0 +1,81 @@
1
+ const ALLOWED_SCHEMES = ['http', 'https'];
2
+ /**
3
+ * v0.1 loopback allowlist: literal localhost/127.0.0.1/::1 and any 127.x.x.x
4
+ * form only. No DNS resolution, no arbitrary named "local dev host" support -
5
+ * an explicit conservative reading recorded in behavior-model.txt.
6
+ */
7
+ function isLoopbackHost(hostname) {
8
+ const normalized = hostname.replace(/^\[|\]$/g, '').toLowerCase();
9
+ if (normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1')
10
+ return true;
11
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized);
12
+ }
13
+ /** Pure classification: scheme + loopback rules only. Never performs DNS resolution or network I/O. */
14
+ export function classifyUrl(url) {
15
+ let parsed;
16
+ try {
17
+ parsed = new URL(url);
18
+ }
19
+ catch {
20
+ return {
21
+ allowed: false,
22
+ diagnostic: { code: 'invalid-request', severity: 'error', message: `malformed URL: ${url}` },
23
+ };
24
+ }
25
+ const scheme = parsed.protocol.replace(/:$/, '');
26
+ if (!ALLOWED_SCHEMES.includes(scheme)) {
27
+ return {
28
+ allowed: false,
29
+ diagnostic: { code: 'unsafe-url', severity: 'error', message: `scheme "${scheme}" is not allowed`, details: { url } },
30
+ };
31
+ }
32
+ if (parsed.username.length > 0 || parsed.password.length > 0) {
33
+ return {
34
+ allowed: false,
35
+ diagnostic: { code: 'unsafe-url', severity: 'error', message: 'credential-bearing URLs are rejected', details: { url } },
36
+ };
37
+ }
38
+ if (!isLoopbackHost(parsed.hostname)) {
39
+ return {
40
+ allowed: false,
41
+ diagnostic: { code: 'unsafe-url', severity: 'error', message: `host "${parsed.hostname}" is not a loopback host`, details: { url } },
42
+ };
43
+ }
44
+ return { allowed: true };
45
+ }
46
+ export function classifyRedirect(fromUrl, toUrl) {
47
+ const decision = classifyUrl(toUrl);
48
+ if (decision.allowed)
49
+ return decision;
50
+ return {
51
+ allowed: false,
52
+ diagnostic: {
53
+ code: 'prohibited-redirect',
54
+ severity: 'error',
55
+ message: 'redirect target failed safety policy',
56
+ details: { from: fromUrl, to: toUrl },
57
+ },
58
+ };
59
+ }
60
+ export function classifySubresource(url) {
61
+ const decision = classifyUrl(url);
62
+ if (decision.allowed)
63
+ return decision;
64
+ return {
65
+ allowed: false,
66
+ diagnostic: {
67
+ code: 'prohibited-subresource-request',
68
+ severity: 'error',
69
+ message: 'subresource target failed safety policy',
70
+ details: { url },
71
+ },
72
+ };
73
+ }
74
+ /** Popups and downloads are never followed/saved in v0.1; both are non-fatal (warning) notices, not errors. */
75
+ export function classifyPopup() {
76
+ return { code: 'unsupported-configuration', severity: 'warning', message: 'popups are not followed in v0.1' };
77
+ }
78
+ export function classifyDownload() {
79
+ return { code: 'unsupported-configuration', severity: 'warning', message: 'downloads are not followed in v0.1' };
80
+ }
81
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/safety/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ # Architecture
2
+
3
+ ## Current scaffold architecture
4
+
5
+ The current repository is one private TypeScript ESM package:
6
+
7
+ - `src/cli.ts` is the real, thin `observe` command entry point (argument
8
+ parsing/output only).
9
+ - `src/index.ts` is the library entry point re-exporting the observer-owned
10
+ contracts/functions from every layer below.
11
+ - `scripts/clean.mjs` safely removes only the project `dist/` directory.
12
+ - `scripts/check-docs.mjs` validates the canonical documentation foundation,
13
+ roadmap version presence, and the no-batches rule.
14
+ - TypeScript, ESLint, Vitest, and package configuration provide foundation
15
+ validation, now exercised by real product tests (`tests/unit/`,
16
+ `tests/browser/`).
17
+
18
+ Batch 1 added the observation domain/schema and safety-policy layer
19
+ (`src/domain/`, `src/request/`, `src/safety/`). Batch 2 added a real
20
+ Playwright Chromium browser adapter (`src/browser/`), a minimal application
21
+ seam invoking it (`src/application/`), and a deterministic browser
22
+ fixture/test boundary (`tests/fixtures/`, `tests/browser/`, run via
23
+ `npm run test:browser`). Batch 3 extended that single browser adapter with an
24
+ internal page/target measurement module (`src/browser/evidenceCapture.ts`)
25
+ that reads page and explicit-CSS-target evidence from the same live,
26
+ already-ready page used for the screenshot - no second browser/page is ever
27
+ opened, and Playwright objects still never leave `src/browser/`. Batch 4
28
+ added the artifact ownership boundary itself: `src/artifacts/artifactWriter.ts`
29
+ is the one canonical place that writes an observation to disk (temp
30
+ directory, then one atomic rename into `<outputLocation>/<observationId>/`),
31
+ and `src/application/observationPersistence.ts` assembles the frozen
32
+ `ObservationArtifact` from a browser-capture result before handing it to the
33
+ writer. The artifact layer has no Playwright dependency and is testable
34
+ without launching Chromium. Batch 5 completed the boundary chain: `src/cli.ts`
35
+ parses `observe` arguments (CLI-syntax errors only - e.g. malformed
36
+ `WIDTHxHEIGHT`), constructs a raw request, and hands it to the existing
37
+ Batch 1 `normalizeRequest`; on success it calls one new application-level use
38
+ case, `observe()` in `src/application/observationPersistence.ts`, which runs
39
+ the existing `runBrowserCapture` exactly once and, only on success, the
40
+ existing artifact writer exactly once, then returns a small observer-owned
41
+ `ApplicationObservationResult` (observation id, completion state, artifact
42
+ path, target/diagnostic counts) for the CLI to print. The CLI never imports
43
+ Playwright or the filesystem-write path directly. Batch 6 closed the
44
+ remaining real-Chromium coverage gap (a genuine navigation failure, distinct
45
+ from a readiness timeout or a pre-launch safety rejection) and validated the
46
+ packed npm tarball end to end in a clean consumer environment, independent
47
+ of the source checkout. There is still no controlled-scroll/comparison
48
+ behavior.
49
+
50
+ ## Planned v0.1 architecture constraints
51
+
52
+ v0.1 planning must preserve these approved boundaries without treating module
53
+ names from the historical run as mandatory:
54
+
55
+ ```text
56
+ thin command-line boundary
57
+
58
+ reusable observation engine/application layer
59
+
60
+ browser automation boundary
61
+
62
+ observer-owned runtime evidence
63
+
64
+ observer-owned domain/schema
65
+
66
+ artifact ownership boundary
67
+
68
+ deterministic fixture/test boundary
69
+
70
+ browser-level validation
71
+ ```
72
+
73
+ Use one browser engine implementation, keep browser logic out of presentation,
74
+ avoid speculative plugin/multi-browser abstractions, and keep observed
75
+ applications external. Before v0.6, versions must not add runtime coupling to
76
+ sibling ecosystem projects. v0.6 may add only explicit bounded context,
77
+ correlation/export, orchestrator-consumption, and lab-compatibility contracts
78
+ while preserving independent ownership.
79
+
80
+ The text/config-driven coding-agent workflow must be operational before the
81
+ viewer and annotation layers are added. Those interfaces consume the same
82
+ canonical observation, relationship, comparison, contract, change-scope,
83
+ correlation, and context boundaries rather than creating parallel engines.
84
+ The concrete implementation plan and module layout must be designed only after
85
+ the relevant version planning workflow inspects the current repositories.
package/docs/CI_CD.md ADDED
@@ -0,0 +1,28 @@
1
+ # CI/CD
2
+
3
+ A GitHub Actions pre-release readiness workflow exists at
4
+ `.github/workflows/pre-release-readiness.yml` (triggered manually via
5
+ `workflow_dispatch` or by pushing a `validation/**` branch). It has two
6
+ phases:
7
+
8
+ 1. **candidate** (Linux, Node 24): `npm ci`, install Chromium, typecheck,
9
+ lint, `npm test`, `npm run test:browser`, `npm run test:security`,
10
+ build, `npm run check:docs`, then `npm pack` to produce exactly one
11
+ candidate tarball and its SHA-256, uploaded as build artifacts.
12
+ 2. **matrix-smoke** (`windows-latest`, `ubuntu-latest`, `macos-latest`, all
13
+ Node 24): each job downloads the *same* candidate tarball produced by the
14
+ candidate job, independently recomputes and verifies its SHA-256 against
15
+ the candidate job's hash (failing immediately on any mismatch - no job
16
+ ever builds its own tarball), installs Chromium via the installed
17
+ package's own Playwright dependency, and runs
18
+ `scripts/ci/runPackedObservationSmoke.mjs` against the installed
19
+ tarball: a real Chromium observation against a disposable local HTTP
20
+ target, with artifact/schema/screenshot/target-immutability assertions.
21
+
22
+ This proves the same packaged candidate installs and performs a real
23
+ observation on Windows, Linux, and macOS, not just in the source checkout.
24
+
25
+ There is no automated npm publication and no automated GitHub Release
26
+ creation - this workflow is readiness validation only, run from a
27
+ `validation/**` branch, never from a release branch or tag. Package
28
+ publication remains a separate, later, explicit release decision.
@@ -0,0 +1,82 @@
1
+ # Commands
2
+
3
+ ## Product command surface
4
+
5
+ `node dist/cli.js observe` (or `my-frontend-observer observe` once installed
6
+ as a bin) captures one bounded, loopback-only browser observation and
7
+ persists it as a portable artifact.
8
+
9
+ ```text
10
+ my-frontend-observer observe --url <loopback-url> [options]
11
+ ```
12
+
13
+ Required:
14
+
15
+ - `--url <url>` — loopback target URL (`http`/`https`; `localhost`,
16
+ `127.x.x.x`, or `::1` only - enforced by the existing request/safety
17
+ contracts, not by CLI-local logic).
18
+
19
+ Options:
20
+
21
+ - `--viewport <WIDTHxHEIGHT>` — e.g. `1280x720`. Malformed syntax (missing
22
+ `x`, non-numeric, empty side) is rejected before any browser launches;
23
+ in-range bounds are enforced by the existing request validator.
24
+ - `--target <id=css-selector>` — an explicit observation target. Repeatable;
25
+ order is preserved. Parsed on the *first* `=` only, so a selector
26
+ containing `=` survives intact, e.g.
27
+ `--target action=button[data-state="active"]`.
28
+ - `--output <directory>` — portable, relative output location for the
29
+ observation artifact (same contract as the request's `outputLocation`; no
30
+ drive letter, no leading `/`, no `..` segments).
31
+ - `--timeout <ms>` — overall request timeout in milliseconds.
32
+ - `--help` — show `observe` usage.
33
+
34
+ Also available: `--help` / `-h` (top-level usage) and `--version` (prints the
35
+ actual package version).
36
+
37
+ On success the command prints exactly:
38
+
39
+ ```text
40
+ Observation: <observation-id>
41
+ State: <complete|partial|warning|fatal|invalid-request>
42
+ Artifact: <artifact-root-path>
43
+ Targets: <configured-target-count>
44
+ Diagnostics: <diagnostic-count>
45
+ ```
46
+
47
+ and exits `0` for a validly persisted observation - including one whose
48
+ `State` truthfully reports `partial` (e.g. a missing or ambiguous target)
49
+ - or exits nonzero for invalid CLI syntax, a request the existing validator
50
+ rejects, an unsafe/failed navigation with no persistable artifact, or a
51
+ failed artifact write. No progress output is printed during a normal
52
+ capture. CLI-syntax errors (e.g. a missing `--url`) print as `error:
53
+ <message>` followed by `observe` usage; request/capture/persistence
54
+ diagnostics print one per line as `[code] message`.
55
+
56
+ ## Foundation commands
57
+
58
+ - `npm install` — install dependencies (includes the `playwright` runtime
59
+ dependency since Batch 2).
60
+ - `npx playwright install chromium` — install the Chromium binary once per
61
+ machine (see `docs/DEVELOPMENT.md`).
62
+ - `npm run typecheck` — run TypeScript no-emit checking.
63
+ - `npm run lint` — lint the repository and scripts.
64
+ - `npm test` — run the fast unit suite (`tests/unit/`).
65
+ - `npm run test:browser` — run the real-Chromium integration suite
66
+ (`tests/browser/`), including a real `observe` end-to-end test against the
67
+ deterministic local fixture.
68
+ - `npm run test:security` — run only the safety-relevant subset of the suite
69
+ (`tests/unit/policy.test.ts` plus the real-Chromium enforcement cases in
70
+ `tests/browser/chromiumAdapter.test.ts`: unsafe initial target, prohibited
71
+ redirect, prohibited subresource request, and browser cleanup around
72
+ safety/navigation failure) — a discoverable entry point for security
73
+ review tooling; it is a subset of, not a replacement for, `npm test` and
74
+ `npm run test:browser`.
75
+ - `npm run build` — clean and compile `src/` (including `src/cli.ts`) to
76
+ `dist/`.
77
+ - `npm run check:docs` — validate canonical documents and roadmap structure.
78
+ - `npm pack --dry-run` — inspect the private package inventory without
79
+ publishing. The real tarball has been installed and exercised in a clean
80
+ temporary consumer directory (real Chromium install, real `observe` run,
81
+ real artifact) as part of v0.1 validation; this is local package
82
+ validation, not a release/publication step.
@@ -0,0 +1,54 @@
1
+ # Contracts
2
+
3
+ ## Current contracts
4
+
5
+ The v0.1 observation artifact contract is implemented (`src/domain/schema.ts`)
6
+ and proven both from the source checkout and from the packed npm tarball:
7
+
8
+ - artifact kind `my-frontend-observer/observation`, schema version `1.0.0`
9
+ (independent of the package version, currently `0.1.0`);
10
+ - one artifact root per observation, `<outputLocation>/<observationId>/`,
11
+ containing exactly `manifest.json` (the full `ObservationArtifact`, with
12
+ page/target evidence embedded inline) and `screenshot.png` - there is no
13
+ separate `evidence.json`;
14
+ - `manifest.json` is written last, after `screenshot.png`, via one atomic
15
+ directory rename, so a consumer never observes a partially-written
16
+ artifact; a filesystem failure anywhere in that sequence reports the
17
+ `artifact-write-failure` diagnostic and leaves no completed artifact;
18
+ - internal artifact references (e.g. `screenshot.png`) are relative to the
19
+ artifact root, never an absolute machine path; the observation's logical
20
+ identity is its `observationId`, not its filesystem location;
21
+ - evidence states `available`, `unavailable`, `not-applicable`, `partial`;
22
+ evidence sources `browser`, `computed-browser`, `derived`;
23
+ - a stable diagnostic vocabulary (`src/domain/diagnostics.ts`) and completion
24
+ states `complete`, `partial`, `warning`, `invalid-request`, `fatal`
25
+ (`src/domain/completion.ts`);
26
+ - observation/request identity, producer/package identity, and browser
27
+ provenance are all present in every persisted manifest.
28
+
29
+ This contract is implemented; it is not yet published as a package, and no
30
+ public programmatic-API compatibility promise has been made.
31
+
32
+ ## Approved v0.1 design inputs
33
+
34
+ The historical greenfield scaffold plan recorded these v0.1 design decisions:
35
+
36
+ - artifact kind `my-frontend-observer/observation`;
37
+ - schema version `1.0.0`, independent of package version;
38
+ - one portable directory containing `manifest.json`, `evidence.json`, and
39
+ `screenshot.png`;
40
+ - evidence states `available`, `unavailable`, `not-applicable`, and `partial`;
41
+ - evidence sources `browser`, `computed-browser`, and `derived`;
42
+ - bounded explicitly requested targets, provenance, diagnostics, completion
43
+ state, limits, and relative artifact references.
44
+
45
+ These were planning inputs only at the time they were recorded. As shown in
46
+ "Current contracts" above, the implemented contract matches them except for
47
+ the file layout: there is no separate `evidence.json` - page/target evidence
48
+ is embedded directly inside `manifest.json`.
49
+
50
+ Comparison and relationship contracts belong to v0.4, and canonical
51
+ change-scope contracts belong to v0.5. Bounded agent-context plus ecosystem
52
+ integration contracts move to v0.6, followed by the text/config-driven
53
+ coding-agent review contract in v0.7. Viewer and annotation contracts follow in
54
+ v0.8 and v0.9 and converge with the existing workflow in v0.10.
@@ -0,0 +1,113 @@
1
+ # Current State
2
+
3
+ The project is published at package version `0.1.0` (roadmap v0.1, Runtime
4
+ Observation Foundation).
5
+
6
+ ## Greenfield foundation established
7
+
8
+ The retained repository contains:
9
+
10
+ - Node.js 24+ and TypeScript ESM package configuration;
11
+ - TypeScript build and typecheck configuration;
12
+ - ESLint configuration;
13
+ - a Vitest runner configured to report honestly when no tests exist;
14
+ - a safe `dist/` clean script;
15
+ - documentation validation;
16
+ - package allowlisting;
17
+ - minimal `src/cli.ts` and `src/index.ts` placeholders required by the selected
18
+ TypeScript CLI starter profile;
19
+ - complete repository-local Project Description, Project Milestones, ROADMAP,
20
+ and standardized documentation.
21
+
22
+ The package bin (`src/cli.ts`) now implements the real `observe` command
23
+ described below; it is no longer the not-implemented placeholder.
24
+
25
+ ## v0.1 progress (Batch 1–6; implemented and released as 0.1.0)
26
+
27
+ - Batch 1 froze and implemented the observation request contract, evidence
28
+ states/sources, schema 1.0.0, observation/request identity, bounded
29
+ readiness semantics, diagnostic/completion semantics, browser/network
30
+ safety policy, and portable path normalization, with 40 passing unit tests.
31
+ - Batch 2 added a Playwright Chromium browser boundary (`src/browser/`) and a
32
+ minimal application seam (`src/application/`) that launches a real
33
+ Chromium browser, enforces the Batch 1 loopback/redirect/subresource
34
+ safety policy at runtime, applies the requested viewport, waits for the
35
+ approved bounded readiness condition, captures a real viewport PNG
36
+ screenshot, returns observer-owned browser provenance, and reliably closes
37
+ the browser on every exit path. Deterministic local HTTP fixtures live
38
+ under `tests/fixtures/`; the real-Chromium integration tests live under
39
+ `tests/browser/` and run via `npm run test:browser` (kept separate from
40
+ `npm test`, which continues to run only the fast unit suite).
41
+ - Batch 3 extended the same single browser observation (no second Chromium
42
+ lifecycle) to also capture the v0.1 minimum page evidence (requested/final
43
+ URL, title, viewport, device pixel ratio, document scroll/client
44
+ dimensions plus a derived overall document width/height, window scroll
45
+ position) and explicit-target evidence (tag, geometry, computed
46
+ display/position/overflow, scroll/client metrics, initial visibility, and
47
+ role/name where the browser reliably exposes them) for every configured
48
+ CSS target, honoring missing/ambiguous-target semantics honestly. This
49
+ additively extended `src/domain/schema.ts`'s `TargetEvidenceRecord` (new
50
+ `tag`/`layout`/`visibility`/`semantics` categories, and concrete shapes for
51
+ `geometry`/`style`) and `BrowserCaptureResult`; schema version stays
52
+ `1.0.0`.
53
+ - Batch 4 added a portable, atomic observation-artifact writer
54
+ (`src/artifacts/artifactWriter.ts`) and a minimal application persistence
55
+ seam (`src/application/observationPersistence.ts`) that assembles the
56
+ frozen `ObservationArtifact` shape from a Batch 2/3 browser capture (using
57
+ the existing Batch 1 identity/completion functions verbatim, no new logic
58
+ invented) and writes it to `<outputLocation>/<observationId>/manifest.json`
59
+ plus `screenshot.png`. Writing happens in a sibling temporary directory
60
+ first (screenshot before manifest), finalized only via one atomic
61
+ directory rename, so a consumer can never observe a partially-written
62
+ artifact under its real name; a filesystem failure anywhere in that
63
+ sequence reports the existing `artifact-write-failure` diagnostic and
64
+ leaves no completed artifact behind. Internal artifact references
65
+ (`screenshot.png`) are relative/portable; the observation's logical
66
+ identity is the existing Batch 1 `observationId`, not its filesystem
67
+ location. The writer has no Playwright dependency and does not modify the
68
+ observed target. Schema stays `1.0.0`.
69
+ - Batch 5 wired the existing owners into the real user-facing workflow:
70
+ `src/cli.ts` implements a real `observe` command (thin argument
71
+ parsing/output only - no Chromium, safety, evidence, or filesystem logic
72
+ of its own), and `src/application/observationPersistence.ts` gained one
73
+ `observe()` use case that runs the existing browser capture exactly once
74
+ and, only on success, persists it exactly once through the existing
75
+ artifact writer. CLI syntax errors (malformed `WIDTHxHEIGHT`, malformed
76
+ `id=selector`) are rejected before any browser launches; all domain bounds
77
+ and safety decisions still come from the existing Batch 1 request
78
+ validator and safety policy, not CLI-local logic. A successfully
79
+ persisted observation - including one whose completion state honestly
80
+ reports `partial` - exits `0`; invalid syntax/request, an unpersistable
81
+ browser failure, or a failed artifact write exits nonzero. Package version
82
+ is `0.1.0`; schema stays `1.0.0`.
83
+
84
+ So: `my-frontend-observer observe --url ... --viewport ... --target ...
85
+ --output ...` is a real, working, source-checkout command that launches
86
+ Chromium, produces bounded runtime evidence, and writes a portable local
87
+ artifact - proven both via `runCli()`-level tests and a built
88
+ `node dist/cli.js observe ...` smoke run against the deterministic fixture.
89
+
90
+ Batch 6 closed the remaining v0.1 coverage gap (a genuine real-Chromium
91
+ navigation failure - connection reset mid-navigation - distinct from a
92
+ readiness timeout or a pre-launch safety rejection) and proved the packaged
93
+ form of the implementation works independent of the source checkout: the
94
+ real `npm pack` tarball, installed fresh in a clean temporary consumer
95
+ directory outside the repository, exposes its `my-frontend-observer` bin,
96
+ reports the correct version/help text, installs its own Chromium binary via
97
+ the consumer-local Playwright toolchain, and performs a real observation
98
+ against a disposable local HTTP target - producing a `manifest.json` +
99
+ `screenshot.png` artifact identical in shape to the source-checkout result,
100
+ without modifying the observed target, and with the temporary consumer/
101
+ tarball/output fully cleaned up afterward. Documentation across the
102
+ repository was reconciled to this implemented state as part of the same
103
+ batch.
104
+
105
+ ## Not implemented
106
+
107
+ - There is no controlled-scroll behavior, target-source correlation, or
108
+ capability beyond the bounded page/target evidence Batches 3-5 established.
109
+ - No v0.2–v0.10 capability is implemented.
110
+
111
+ ## Next target
112
+
113
+ v0.1.0 is released. The next allowed workflow is v0.2 planning.