viewspec 0.3.0b2__tar.gz → 0.3.0b4__tar.gz
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.
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/.gitignore +2 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/PKG-INFO +25 -4
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/README.md +21 -2
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/pyproject.toml +4 -2
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/__init__.py +140 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/_version.py +1 -1
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_react_verify.py +47 -7
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/cli.py +64 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/compiler.py +64 -13
- viewspec-0.3.0b4/src/viewspec/conformance.py +285 -0
- viewspec-0.3.0b4/src/viewspec/convergence.py +227 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/error_codes.py +12 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify.py +249 -23
- viewspec-0.3.0b4/src/viewspec/host_verify_template/playwright.config.ts +52 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/tests/host-verify.spec.ts +264 -14
- viewspec-0.3.0b4/src/viewspec/hosted_app_bundles.py +310 -0
- viewspec-0.3.0b4/src/viewspec/hosted_artifacts.py +283 -0
- viewspec-0.3.0b4/src/viewspec/hosted_receipts.py +84 -0
- viewspec-0.3.0b4/src/viewspec/hosted_verification.py +453 -0
- viewspec-0.3.0b4/src/viewspec/intent_contract.py +62 -0
- viewspec-0.3.0b4/src/viewspec/local_verify.py +249 -0
- viewspec-0.3.0b4/src/viewspec/node_runtime.py +27 -0
- viewspec-0.3.0b4/src/viewspec/repair.py +358 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/types.py +7 -13
- viewspec-0.3.0b4/src/viewspec/verification.py +452 -0
- viewspec-0.3.0b2/src/viewspec/host_verify_template/playwright.config.ts +0 -25
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/LICENSE +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/__main__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/a11y.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/aesthetics.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/agent.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/agent_assets.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_agent_schema.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_bundle.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_diff.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_errors.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_paths.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_pipeline.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_prepared.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_react.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_reports.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_resource_binding.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_screens.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_shell.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_shell_writer.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_starters.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_state_artifacts.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_tools.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_validation.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/app_visibility.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/compiler_benchmarks.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/compiler_refs.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/design_md.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/base.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/html_tailwind/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/react_tailwind_tsx/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/react_tailwind_tsx/a11y.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/react_tailwind_tsx/recipes.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/emitters/react_tsx/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/index.html +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/package-lock.json +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/package.json +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/src/App.tsx +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/src/index.css +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/src/main.tsx +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/tsconfig.json +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/host_verify_template/vite.config.ts +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/intent_tools.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools_constants.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools_hash.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools_io.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools_response.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/local_tools_validators.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/manifest_summary.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/mcp_server.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/motif_compilers.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/motif_plugins.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/native_agents.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/prove.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/py.typed +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/raw_html.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/schema/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/schema/viewspec_pb2.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/sdk/__init__.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/sdk/builder.py +0 -0
- {viewspec-0.3.0b2 → viewspec-0.3.0b4}/src/viewspec/state_ir.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: viewspec
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.0b4
|
|
4
4
|
Summary: Agent-native UI compiler. Agents describe intent; ViewSpec compiles concrete UI.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nxrobins/viewspec
|
|
6
6
|
Project-URL: Repository, https://github.com/nxrobins/viewspec
|
|
@@ -9,7 +9,7 @@ Author: Nigel Robinson
|
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Keywords: agents,compiler,composition-ir,declarative,protobuf,semantic,ui,viewspec
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -28,10 +28,12 @@ Requires-Dist: mcp>=1.9.0; extra == 'agents'
|
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: build>=1.2; extra == 'dev'
|
|
30
30
|
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: hypothesis<7,>=6.100; extra == 'dev'
|
|
31
32
|
Requires-Dist: jsonschema>=4.18; extra == 'dev'
|
|
32
33
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
34
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
34
35
|
Provides-Extra: remote
|
|
36
|
+
Requires-Dist: cryptography<49,>=48.0.1; extra == 'remote'
|
|
35
37
|
Requires-Dist: httpx>=0.27.0; extra == 'remote'
|
|
36
38
|
Description-Content-Type: text/markdown
|
|
37
39
|
|
|
@@ -39,7 +41,7 @@ Description-Content-Type: text/markdown
|
|
|
39
41
|
|
|
40
42
|
**Stop asking agents to write React, DOM, or State.**
|
|
41
43
|
|
|
42
|
-
ViewSpec is an agent-native UI compiler. It acts as a universal Intermediate Representation (IR) for AI Software Engineering. Agents describe UI and state intent as `IntentBundle` or `AppBundle` JSON; ViewSpec compiles that intent into deterministic HTML and React surfaces with the free local compiler,
|
|
44
|
+
ViewSpec is an agent-native UI compiler. It acts as a universal Intermediate Representation (IR) for AI Software Engineering. Agents describe UI and state intent as `IntentBundle` or `AppBundle` JSON; ViewSpec compiles that intent into deterministic HTML and React surfaces with the free local compiler. The paid hosted API adds Level 2 derivation, DESIGN.md context, projections, rules, custom motifs, signed usage receipts, integrity-checked single-screen artifacts, and verified runnable AppBundle projects.
|
|
43
45
|
|
|
44
46
|
By totally decoupling intent from implementation, ViewSpec eliminates the "visual context window" problem. Agents no longer need to see pixels to build complex, perfectly styled, and deeply interactive applications.
|
|
45
47
|
|
|
@@ -219,7 +221,20 @@ viewspec mcp
|
|
|
219
221
|
```
|
|
220
222
|
The MCP server exposes all intent-first local tools without requiring shell commands, including `validate_intent_bundle_file`, `compile_intent_bundle_file`, `verify_host`, `prove`, `validate_app_file`, `diff_app_files`, `compile_app`, and `prove_app`.
|
|
221
223
|
|
|
222
|
-
For
|
|
224
|
+
For rendered conformance, compile React/Tailwind TSX and run:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
viewspec verify react-tailwind-output/ --install --json
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The verifier renders canonical mobile, tablet, and desktop viewports and writes integrity-addressed
|
|
231
|
+
screenshots, DOM snapshots, accessibility evidence, `result.json`, and a deterministic `repair.json`.
|
|
232
|
+
Exit codes distinguish conformant (`0`), nonconformant (`1`), and indeterminate (`2`) results.
|
|
233
|
+
`viewspec verify-host` remains the lower-level bounded host assertion proof for
|
|
234
|
+
grid column/span counts, profiled aesthetic markers/layout, and action payload behavior.
|
|
235
|
+
Its JSON `assertion_requirements` records `dom_count`, `style_assertion_count`, and
|
|
236
|
+
`aesthetic_layout_assertion_count` expectations from the checked manifest.
|
|
237
|
+
`viewspec prove` combines compilation checks with that proof.
|
|
223
238
|
|
|
224
239
|
## viewspec.dev
|
|
225
240
|
|
|
@@ -300,6 +315,12 @@ ast = compile_auto(builder.build_bundle())
|
|
|
300
315
|
|
|
301
316
|
The hosted fallback requires the `remote` extra: `python -m pip install "viewspec[remote]"` (adds `httpx`). Without it, `compile_auto` runs locally and raises `ImportError` only if a hosted fallback is actually needed.
|
|
302
317
|
|
|
318
|
+
Paid agents can submit a complete AppBundle to `submit_verification_remote(...)` for compiled
|
|
319
|
+
route/state proof plus per-screen browser evidence and a signed receipt. For bounded autonomous
|
|
320
|
+
repair, `compile_until_conformant_remote(...)` repeats that paid compile-and-verify step while a
|
|
321
|
+
caller-owned repair callback edits the semantic AppBundle. ViewSpec enforces lineage, plan
|
|
322
|
+
stability, attempt limits, and no-progress termination.
|
|
323
|
+
|
|
303
324
|
### Theming with DESIGN.md
|
|
304
325
|
The local SDK uses a strict YAML-front-matter `DESIGN.md` for offline HTML and IntentBundle compilation. The API requires exact sRGB hex values (e.g., `#FFFFFF`), enforcing strict design token discipline.
|
|
305
326
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Stop asking agents to write React, DOM, or State.**
|
|
4
4
|
|
|
5
|
-
ViewSpec is an agent-native UI compiler. It acts as a universal Intermediate Representation (IR) for AI Software Engineering. Agents describe UI and state intent as `IntentBundle` or `AppBundle` JSON; ViewSpec compiles that intent into deterministic HTML and React surfaces with the free local compiler,
|
|
5
|
+
ViewSpec is an agent-native UI compiler. It acts as a universal Intermediate Representation (IR) for AI Software Engineering. Agents describe UI and state intent as `IntentBundle` or `AppBundle` JSON; ViewSpec compiles that intent into deterministic HTML and React surfaces with the free local compiler. The paid hosted API adds Level 2 derivation, DESIGN.md context, projections, rules, custom motifs, signed usage receipts, integrity-checked single-screen artifacts, and verified runnable AppBundle projects.
|
|
6
6
|
|
|
7
7
|
By totally decoupling intent from implementation, ViewSpec eliminates the "visual context window" problem. Agents no longer need to see pixels to build complex, perfectly styled, and deeply interactive applications.
|
|
8
8
|
|
|
@@ -182,7 +182,20 @@ viewspec mcp
|
|
|
182
182
|
```
|
|
183
183
|
The MCP server exposes all intent-first local tools without requiring shell commands, including `validate_intent_bundle_file`, `compile_intent_bundle_file`, `verify_host`, `prove`, `validate_app_file`, `diff_app_files`, `compile_app`, and `prove_app`.
|
|
184
184
|
|
|
185
|
-
For
|
|
185
|
+
For rendered conformance, compile React/Tailwind TSX and run:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
viewspec verify react-tailwind-output/ --install --json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The verifier renders canonical mobile, tablet, and desktop viewports and writes integrity-addressed
|
|
192
|
+
screenshots, DOM snapshots, accessibility evidence, `result.json`, and a deterministic `repair.json`.
|
|
193
|
+
Exit codes distinguish conformant (`0`), nonconformant (`1`), and indeterminate (`2`) results.
|
|
194
|
+
`viewspec verify-host` remains the lower-level bounded host assertion proof for
|
|
195
|
+
grid column/span counts, profiled aesthetic markers/layout, and action payload behavior.
|
|
196
|
+
Its JSON `assertion_requirements` records `dom_count`, `style_assertion_count`, and
|
|
197
|
+
`aesthetic_layout_assertion_count` expectations from the checked manifest.
|
|
198
|
+
`viewspec prove` combines compilation checks with that proof.
|
|
186
199
|
|
|
187
200
|
## viewspec.dev
|
|
188
201
|
|
|
@@ -263,6 +276,12 @@ ast = compile_auto(builder.build_bundle())
|
|
|
263
276
|
|
|
264
277
|
The hosted fallback requires the `remote` extra: `python -m pip install "viewspec[remote]"` (adds `httpx`). Without it, `compile_auto` runs locally and raises `ImportError` only if a hosted fallback is actually needed.
|
|
265
278
|
|
|
279
|
+
Paid agents can submit a complete AppBundle to `submit_verification_remote(...)` for compiled
|
|
280
|
+
route/state proof plus per-screen browser evidence and a signed receipt. For bounded autonomous
|
|
281
|
+
repair, `compile_until_conformant_remote(...)` repeats that paid compile-and-verify step while a
|
|
282
|
+
caller-owned repair callback edits the semantic AppBundle. ViewSpec enforces lineage, plan
|
|
283
|
+
stability, attempt limits, and no-progress termination.
|
|
284
|
+
|
|
266
285
|
### Theming with DESIGN.md
|
|
267
286
|
The local SDK uses a strict YAML-front-matter `DESIGN.md` for offline HTML and IntentBundle compilation. The API requires exact sRGB hex values (e.g., `#FFFFFF`), enforcing strict design token discipline.
|
|
268
287
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "viewspec"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.0b4"
|
|
8
8
|
description = "Agent-native UI compiler. Agents describe intent; ViewSpec compiles concrete UI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -14,7 +14,7 @@ authors = [
|
|
|
14
14
|
]
|
|
15
15
|
keywords = ["ui", "compiler", "semantic", "viewspec", "composition-ir", "protobuf", "declarative", "agents"]
|
|
16
16
|
classifiers = [
|
|
17
|
-
"Development Status ::
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
19
19
|
"License :: OSI Approved :: MIT License",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
@@ -38,12 +38,14 @@ viewspec = "viewspec.cli:main"
|
|
|
38
38
|
dev = [
|
|
39
39
|
"build>=1.2",
|
|
40
40
|
"pytest>=8.0",
|
|
41
|
+
"hypothesis>=6.100,<7",
|
|
41
42
|
"ruff>=0.4",
|
|
42
43
|
"httpx>=0.27.0",
|
|
43
44
|
"jsonschema>=4.18",
|
|
44
45
|
]
|
|
45
46
|
remote = [
|
|
46
47
|
"httpx>=0.27.0",
|
|
48
|
+
"cryptography>=48.0.1,<49",
|
|
47
49
|
]
|
|
48
50
|
agents = [
|
|
49
51
|
"mcp>=1.9.0",
|
|
@@ -46,6 +46,64 @@ from viewspec.compiler import (
|
|
|
46
46
|
CompilerInputError,
|
|
47
47
|
UnsupportedMotifError,
|
|
48
48
|
)
|
|
49
|
+
from viewspec.conformance import (
|
|
50
|
+
CONFORMANCE_CORPUS_SCHEMA_VERSION,
|
|
51
|
+
ConformanceCase,
|
|
52
|
+
ConformanceCorpus,
|
|
53
|
+
load_conformance_corpus,
|
|
54
|
+
run_conformance_corpus,
|
|
55
|
+
)
|
|
56
|
+
from viewspec.convergence import (
|
|
57
|
+
CONVERGENCE_SCHEMA_VERSION,
|
|
58
|
+
ConvergenceAttempt,
|
|
59
|
+
ConvergenceRun,
|
|
60
|
+
MAX_CONVERGENCE_ATTEMPTS,
|
|
61
|
+
run_until_conformant,
|
|
62
|
+
)
|
|
63
|
+
from viewspec.hosted_artifacts import (
|
|
64
|
+
ARTIFACT_SCHEMA_VERSION,
|
|
65
|
+
ARTIFACT_TARGETS,
|
|
66
|
+
ArtifactContractError,
|
|
67
|
+
ArtifactCompilerIdentity,
|
|
68
|
+
ArtifactFile,
|
|
69
|
+
ArtifactProvenance,
|
|
70
|
+
ArtifactResponse,
|
|
71
|
+
ArtifactUsage,
|
|
72
|
+
compile_artifact_remote,
|
|
73
|
+
)
|
|
74
|
+
from viewspec.hosted_receipts import RECEIPT_ALGORITHM, ReceiptPublicKey, verify_usage_receipt
|
|
75
|
+
from viewspec.hosted_receipts import verify_signed_receipt
|
|
76
|
+
from viewspec.hosted_app_bundles import (
|
|
77
|
+
APP_BUILD_MANIFEST,
|
|
78
|
+
APP_BUILD_SCHEMA_VERSION,
|
|
79
|
+
APP_BUILD_TARGET,
|
|
80
|
+
AppBuildFile,
|
|
81
|
+
AppBuildProvenance,
|
|
82
|
+
AppBuildUsage,
|
|
83
|
+
AppBundleBuildContractError,
|
|
84
|
+
AppBundleBuildResponse,
|
|
85
|
+
compile_app_remote,
|
|
86
|
+
)
|
|
87
|
+
from viewspec.hosted_verification import (
|
|
88
|
+
HOSTED_VERIFICATION_SCHEMA_VERSION,
|
|
89
|
+
HOSTED_VERIFICATION_TARGET,
|
|
90
|
+
HostedEvidenceArtifact,
|
|
91
|
+
HostedVerificationContractError,
|
|
92
|
+
HostedVerificationError,
|
|
93
|
+
HostedVerificationJob,
|
|
94
|
+
HostedVerificationUsage,
|
|
95
|
+
build_verification_job_id,
|
|
96
|
+
compile_until_conformant_remote,
|
|
97
|
+
get_verification_remote,
|
|
98
|
+
submit_verification_remote,
|
|
99
|
+
verification_request_payload,
|
|
100
|
+
wait_verification_remote,
|
|
101
|
+
)
|
|
102
|
+
from viewspec.intent_contract import (
|
|
103
|
+
IntentBundleContractError,
|
|
104
|
+
normalize_intent_bundle_payload,
|
|
105
|
+
validate_intent_bundle_schema_version,
|
|
106
|
+
)
|
|
49
107
|
from viewspec.motif_plugins import (
|
|
50
108
|
MOTIF_PLUGIN_ABI_VERSION,
|
|
51
109
|
MotifCompileContext,
|
|
@@ -171,6 +229,24 @@ from viewspec.host_verify import (
|
|
|
171
229
|
verify_host_intent_file,
|
|
172
230
|
verify_host_tool,
|
|
173
231
|
)
|
|
232
|
+
from viewspec.local_verify import BrowserEvidence, BrowserPlanOutcome, verify_local_artifact
|
|
233
|
+
from viewspec.repair import (
|
|
234
|
+
REPAIR_SCHEMA_VERSION,
|
|
235
|
+
RepairDirective,
|
|
236
|
+
SourceNodePath,
|
|
237
|
+
VerificationRepairPlan,
|
|
238
|
+
)
|
|
239
|
+
from viewspec.verification import (
|
|
240
|
+
DEFAULT_VIEWPORTS,
|
|
241
|
+
EvidenceFile,
|
|
242
|
+
RetryLineage,
|
|
243
|
+
VERIFICATION_SCHEMA_VERSION,
|
|
244
|
+
VerificationDiagnostic,
|
|
245
|
+
VerificationPlan,
|
|
246
|
+
VerificationResult,
|
|
247
|
+
VerificationViewport,
|
|
248
|
+
build_verification_id,
|
|
249
|
+
)
|
|
174
250
|
from viewspec.prove import prove, prove_tool
|
|
175
251
|
|
|
176
252
|
__all__ = [
|
|
@@ -184,6 +260,12 @@ __all__ = [
|
|
|
184
260
|
"CompileResponseMeta",
|
|
185
261
|
"CompilerDiagnostic",
|
|
186
262
|
"CompilerResult",
|
|
263
|
+
"CONFORMANCE_CORPUS_SCHEMA_VERSION",
|
|
264
|
+
"CONVERGENCE_SCHEMA_VERSION",
|
|
265
|
+
"ConformanceCase",
|
|
266
|
+
"ConformanceCorpus",
|
|
267
|
+
"ConvergenceAttempt",
|
|
268
|
+
"ConvergenceRun",
|
|
187
269
|
"CompositionIR",
|
|
188
270
|
"DesignFinding",
|
|
189
271
|
"DesignMetadata",
|
|
@@ -214,6 +296,44 @@ __all__ = [
|
|
|
214
296
|
"CompilerAPIError",
|
|
215
297
|
"CompilerInputError",
|
|
216
298
|
"UnsupportedMotifError",
|
|
299
|
+
"ARTIFACT_SCHEMA_VERSION",
|
|
300
|
+
"ARTIFACT_TARGETS",
|
|
301
|
+
"ArtifactContractError",
|
|
302
|
+
"ArtifactCompilerIdentity",
|
|
303
|
+
"ArtifactFile",
|
|
304
|
+
"ArtifactProvenance",
|
|
305
|
+
"ArtifactResponse",
|
|
306
|
+
"ArtifactUsage",
|
|
307
|
+
"compile_artifact_remote",
|
|
308
|
+
"RECEIPT_ALGORITHM",
|
|
309
|
+
"ReceiptPublicKey",
|
|
310
|
+
"verify_usage_receipt",
|
|
311
|
+
"verify_signed_receipt",
|
|
312
|
+
"APP_BUILD_MANIFEST",
|
|
313
|
+
"APP_BUILD_SCHEMA_VERSION",
|
|
314
|
+
"APP_BUILD_TARGET",
|
|
315
|
+
"AppBuildFile",
|
|
316
|
+
"AppBuildProvenance",
|
|
317
|
+
"AppBuildUsage",
|
|
318
|
+
"AppBundleBuildContractError",
|
|
319
|
+
"AppBundleBuildResponse",
|
|
320
|
+
"compile_app_remote",
|
|
321
|
+
"HOSTED_VERIFICATION_SCHEMA_VERSION",
|
|
322
|
+
"HOSTED_VERIFICATION_TARGET",
|
|
323
|
+
"HostedEvidenceArtifact",
|
|
324
|
+
"HostedVerificationContractError",
|
|
325
|
+
"HostedVerificationError",
|
|
326
|
+
"HostedVerificationJob",
|
|
327
|
+
"HostedVerificationUsage",
|
|
328
|
+
"build_verification_job_id",
|
|
329
|
+
"compile_until_conformant_remote",
|
|
330
|
+
"get_verification_remote",
|
|
331
|
+
"submit_verification_remote",
|
|
332
|
+
"verification_request_payload",
|
|
333
|
+
"wait_verification_remote",
|
|
334
|
+
"IntentBundleContractError",
|
|
335
|
+
"normalize_intent_bundle_payload",
|
|
336
|
+
"validate_intent_bundle_schema_version",
|
|
217
337
|
"MOTIF_PLUGIN_ABI_VERSION",
|
|
218
338
|
"MotifCompileContext",
|
|
219
339
|
"MotifCompileResult",
|
|
@@ -325,11 +445,31 @@ __all__ = [
|
|
|
325
445
|
"verify_host_artifact_dir",
|
|
326
446
|
"verify_host_intent_file",
|
|
327
447
|
"verify_host_tool",
|
|
448
|
+
"BrowserEvidence",
|
|
449
|
+
"BrowserPlanOutcome",
|
|
450
|
+
"verify_local_artifact",
|
|
451
|
+
"DEFAULT_VIEWPORTS",
|
|
452
|
+
"EvidenceFile",
|
|
453
|
+
"RetryLineage",
|
|
454
|
+
"REPAIR_SCHEMA_VERSION",
|
|
455
|
+
"RepairDirective",
|
|
456
|
+
"SourceNodePath",
|
|
457
|
+
"VERIFICATION_SCHEMA_VERSION",
|
|
458
|
+
"VerificationDiagnostic",
|
|
459
|
+
"VerificationPlan",
|
|
460
|
+
"VerificationResult",
|
|
461
|
+
"VerificationRepairPlan",
|
|
462
|
+
"VerificationViewport",
|
|
463
|
+
"build_verification_id",
|
|
328
464
|
"prove",
|
|
329
465
|
"prove_tool",
|
|
330
466
|
# Utilities
|
|
331
467
|
"build_address_index",
|
|
332
468
|
"normalize_compiler_result",
|
|
469
|
+
"load_conformance_corpus",
|
|
470
|
+
"MAX_CONVERGENCE_ATTEMPTS",
|
|
333
471
|
"parse_canonical_address",
|
|
334
472
|
"resolve_address",
|
|
473
|
+
"run_conformance_corpus",
|
|
474
|
+
"run_until_conformant",
|
|
335
475
|
]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import os
|
|
6
7
|
import shutil
|
|
7
8
|
import subprocess
|
|
8
9
|
import tempfile
|
|
@@ -12,6 +13,7 @@ from typing import Any
|
|
|
12
13
|
|
|
13
14
|
from viewspec.app_react import REACT_APP_MANIFEST, REACT_APP_TARGET
|
|
14
15
|
from viewspec.local_tools import file_hash
|
|
16
|
+
from viewspec.node_runtime import materialize_prebuilt_node_modules
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
APP_REACT_VERIFY_SCHEMA_VERSION = 1
|
|
@@ -43,12 +45,20 @@ def verify_react_app_artifact_dir(
|
|
|
43
45
|
host_dir = Path(temp_name) / "app"
|
|
44
46
|
_copy_artifact(artifact_path, host_dir)
|
|
45
47
|
if install:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
seed = os.environ.get("VIEWSPEC_HOST_VERIFY_NODE_MODULES_DIR")
|
|
49
|
+
if seed:
|
|
50
|
+
_link_prebuilt_node_modules(host_dir, Path(seed))
|
|
51
|
+
else:
|
|
52
|
+
install_result = _run_process(
|
|
53
|
+
["npm", "ci", "--ignore-scripts"],
|
|
54
|
+
cwd=host_dir,
|
|
55
|
+
timeout=APP_REACT_VERIFY_INSTALL_TIMEOUT_SECONDS,
|
|
56
|
+
)
|
|
57
|
+
_require_success(
|
|
58
|
+
install_result,
|
|
59
|
+
"APP_REACT_VERIFY_INSTALL_FAILED",
|
|
60
|
+
"npm ci --ignore-scripts",
|
|
61
|
+
)
|
|
52
62
|
else:
|
|
53
63
|
source_modules = artifact_path / "node_modules"
|
|
54
64
|
if not source_modules.is_dir():
|
|
@@ -87,7 +97,13 @@ def verify_react_app_artifact_dir(
|
|
|
87
97
|
"install": bool(install),
|
|
88
98
|
"assertions": assertions,
|
|
89
99
|
"policy": {
|
|
90
|
-
"install_command":
|
|
100
|
+
"install_command": (
|
|
101
|
+
"prebuilt_node_modules"
|
|
102
|
+
if install and os.environ.get("VIEWSPEC_HOST_VERIFY_NODE_MODULES_DIR")
|
|
103
|
+
else "npm ci --ignore-scripts"
|
|
104
|
+
if install
|
|
105
|
+
else "none"
|
|
106
|
+
),
|
|
91
107
|
"build_command": "npm run build",
|
|
92
108
|
"browser_command": "npm run viewspec:verify",
|
|
93
109
|
},
|
|
@@ -122,6 +138,30 @@ class ReactAppVerifyFailure(ValueError):
|
|
|
122
138
|
self.fix = fix
|
|
123
139
|
|
|
124
140
|
|
|
141
|
+
def _link_prebuilt_node_modules(host_dir: Path, configured: Path) -> None:
|
|
142
|
+
if not configured.is_absolute():
|
|
143
|
+
raise ReactAppVerifyFailure(
|
|
144
|
+
"APP_REACT_VERIFY_DEPENDENCIES_MISSING",
|
|
145
|
+
"VIEWSPEC_HOST_VERIFY_NODE_MODULES_DIR must be an absolute path.",
|
|
146
|
+
"Configure an absolute prebuilt node_modules path and retry.",
|
|
147
|
+
)
|
|
148
|
+
seed = configured.resolve()
|
|
149
|
+
if not seed.is_dir():
|
|
150
|
+
raise ReactAppVerifyFailure(
|
|
151
|
+
"APP_REACT_VERIFY_DEPENDENCIES_MISSING",
|
|
152
|
+
"Configured prebuilt node_modules directory does not exist.",
|
|
153
|
+
"Build the configured dependency bundle and retry.",
|
|
154
|
+
)
|
|
155
|
+
destination = host_dir / "node_modules"
|
|
156
|
+
if destination.exists() or destination.is_symlink():
|
|
157
|
+
raise ReactAppVerifyFailure(
|
|
158
|
+
"APP_REACT_VERIFY_DEPENDENCIES_MISSING",
|
|
159
|
+
"Host node_modules destination must be empty before linking dependencies.",
|
|
160
|
+
"Remove the existing host dependency directory and retry.",
|
|
161
|
+
)
|
|
162
|
+
materialize_prebuilt_node_modules(destination, seed)
|
|
163
|
+
|
|
164
|
+
|
|
125
165
|
def _preflight(artifact_dir: Path, manifest_path: Path) -> dict[str, Any]:
|
|
126
166
|
if not artifact_dir.is_dir() or not manifest_path.is_file():
|
|
127
167
|
raise ReactAppVerifyFailure(
|
|
@@ -55,6 +55,8 @@ from viewspec.local_tools import (
|
|
|
55
55
|
init_design_file,
|
|
56
56
|
source_hash,
|
|
57
57
|
)
|
|
58
|
+
from viewspec.local_verify import verify_local_artifact
|
|
59
|
+
from viewspec.verification import VerificationPlan
|
|
58
60
|
from viewspec.prove import PROVE_DEFAULT_OUT, prove
|
|
59
61
|
from viewspec.mcp_server import MCP_INSTALL_HINT, MissingMCPDependency, mcp_dependency_available, run_mcp_server
|
|
60
62
|
from viewspec.native_agents import NativeAgentError, VALID_TARGETS, init_agent_instructions
|
|
@@ -275,6 +277,27 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
275
277
|
verify_host_parser.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
|
|
276
278
|
verify_host_parser.set_defaults(func=_verify_host_command)
|
|
277
279
|
|
|
280
|
+
verify_parser = subparsers.add_parser("verify", help="Verify rendered UI conformance across canonical viewports.")
|
|
281
|
+
verify_parser.add_argument("artifact_dir", help="Checked react-tailwind-tsx artifact directory.")
|
|
282
|
+
verify_parser.add_argument("--plan", help="Optional verification plan JSON file.")
|
|
283
|
+
verify_parser.add_argument(
|
|
284
|
+
"--evidence-out",
|
|
285
|
+
default=".viewspec-verification/evidence",
|
|
286
|
+
help="Empty directory where browser evidence will be written.",
|
|
287
|
+
)
|
|
288
|
+
verify_parser.add_argument(
|
|
289
|
+
"--report-out",
|
|
290
|
+
default=".viewspec-verification/result.json",
|
|
291
|
+
help="Canonical verification result JSON path.",
|
|
292
|
+
)
|
|
293
|
+
verify_parser.add_argument(
|
|
294
|
+
"--repair-out",
|
|
295
|
+
help="Repair plan JSON path. Defaults to repair.json beside --report-out.",
|
|
296
|
+
)
|
|
297
|
+
verify_parser.add_argument("--install", action="store_true", help="Allow npm ci in the isolated browser host.")
|
|
298
|
+
verify_parser.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
|
|
299
|
+
verify_parser.set_defaults(func=_verify_command)
|
|
300
|
+
|
|
278
301
|
init_agent_parser = subparsers.add_parser("init-agent", help="Install managed ViewSpec instructions for coding agents.")
|
|
279
302
|
init_agent_parser.add_argument("--target", required=True, choices=VALID_TARGETS, help="Agent instruction target to update.")
|
|
280
303
|
init_agent_parser.add_argument("--root", default=".", help="Repository root for instruction files.")
|
|
@@ -1076,6 +1099,47 @@ def _verify_host_command(args: argparse.Namespace) -> int:
|
|
|
1076
1099
|
return 0 if result["ok"] else 2
|
|
1077
1100
|
|
|
1078
1101
|
|
|
1102
|
+
def _verify_command(args: argparse.Namespace) -> int:
|
|
1103
|
+
plan = VerificationPlan.default()
|
|
1104
|
+
if args.plan:
|
|
1105
|
+
plan = VerificationPlan.from_json(json.loads(Path(args.plan).read_text(encoding="utf-8")))
|
|
1106
|
+
result = verify_local_artifact(
|
|
1107
|
+
args.artifact_dir,
|
|
1108
|
+
plan=plan,
|
|
1109
|
+
evidence_dir=args.evidence_out,
|
|
1110
|
+
report_out=args.report_out,
|
|
1111
|
+
install=bool(args.install),
|
|
1112
|
+
)
|
|
1113
|
+
from viewspec.repair import VerificationRepairPlan
|
|
1114
|
+
|
|
1115
|
+
repair = VerificationRepairPlan.from_result(result)
|
|
1116
|
+
repair_path = (
|
|
1117
|
+
Path(args.repair_out)
|
|
1118
|
+
if args.repair_out
|
|
1119
|
+
else Path(args.report_out).with_name("repair.json")
|
|
1120
|
+
)
|
|
1121
|
+
atomic_write(
|
|
1122
|
+
repair_path,
|
|
1123
|
+
json.dumps(repair.to_json(), indent=2, sort_keys=True) + "\n",
|
|
1124
|
+
)
|
|
1125
|
+
payload = {**result.to_json(), "repair_plan": repair.to_json()}
|
|
1126
|
+
if args.json:
|
|
1127
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
1128
|
+
else:
|
|
1129
|
+
print(result.status)
|
|
1130
|
+
print(f"verification_id: {result.verification_id}")
|
|
1131
|
+
print(f"evidence_files: {len(result.evidence)}")
|
|
1132
|
+
print(f"next: {repair.disposition}")
|
|
1133
|
+
for diagnostic in result.diagnostics:
|
|
1134
|
+
location = f" [{diagnostic.viewport}]" if diagnostic.viewport else ""
|
|
1135
|
+
source = f" {diagnostic.source_ref}" if diagnostic.source_ref else ""
|
|
1136
|
+
print(f"{diagnostic.severity}: {diagnostic.code}{location}{source}: {diagnostic.message}")
|
|
1137
|
+
for directive in repair.directives:
|
|
1138
|
+
source = f" {directive.source_path.to_text()}" if directive.source_path else ""
|
|
1139
|
+
print(f"repair: {directive.repair_id}{source}: {directive.instruction}")
|
|
1140
|
+
return {"conformant": 0, "nonconformant": 1, "indeterminate": 2}[result.status]
|
|
1141
|
+
|
|
1142
|
+
|
|
1079
1143
|
def _host_assertion_summary_lines(assertions: object, requirements: object = None) -> list[str]:
|
|
1080
1144
|
if not isinstance(assertions, dict):
|
|
1081
1145
|
return []
|
|
@@ -1803,8 +1803,66 @@ VIEWSPEC_API_URL = "https://api.viewspec.dev"
|
|
|
1803
1803
|
|
|
1804
1804
|
|
|
1805
1805
|
class CompilerAPIError(Exception):
|
|
1806
|
-
"""
|
|
1807
|
-
|
|
1806
|
+
"""Structured failure returned by the hosted compiler API."""
|
|
1807
|
+
|
|
1808
|
+
def __init__(
|
|
1809
|
+
self,
|
|
1810
|
+
message: str,
|
|
1811
|
+
*,
|
|
1812
|
+
code: str = "remote_error",
|
|
1813
|
+
path: str | None = None,
|
|
1814
|
+
status_code: int | None = None,
|
|
1815
|
+
request_id: str | None = None,
|
|
1816
|
+
retry_after: str | None = None,
|
|
1817
|
+
) -> None:
|
|
1818
|
+
super().__init__(message)
|
|
1819
|
+
self.code = code
|
|
1820
|
+
self.path = path
|
|
1821
|
+
self.status_code = status_code
|
|
1822
|
+
self.request_id = request_id
|
|
1823
|
+
self.retry_after = retry_after
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def _response_header(response: Any, name: str) -> str | None:
|
|
1827
|
+
headers = getattr(response, "headers", {})
|
|
1828
|
+
value = headers.get(name) if hasattr(headers, "get") else None
|
|
1829
|
+
if value is None and hasattr(headers, "items"):
|
|
1830
|
+
value = next((item for key, item in headers.items() if str(key).lower() == name.lower()), None)
|
|
1831
|
+
return None if value is None else str(value)
|
|
1832
|
+
|
|
1833
|
+
|
|
1834
|
+
def _compiler_api_error(response: Any, data: Any = None) -> CompilerAPIError:
|
|
1835
|
+
status_code = int(getattr(response, "status_code", 0)) or None
|
|
1836
|
+
if data is None:
|
|
1837
|
+
try:
|
|
1838
|
+
data = response.json()
|
|
1839
|
+
except ValueError:
|
|
1840
|
+
data = None
|
|
1841
|
+
error = data.get("error") if isinstance(data, dict) else None
|
|
1842
|
+
structured = error if isinstance(error, dict) else {}
|
|
1843
|
+
message = str(structured.get("message") or "")
|
|
1844
|
+
code = str(structured.get("code") or "remote_error")
|
|
1845
|
+
path = structured.get("path") if isinstance(structured.get("path"), str) else None
|
|
1846
|
+
if not message and isinstance(data, dict) and isinstance(data.get("message"), str):
|
|
1847
|
+
message = data["message"]
|
|
1848
|
+
if status_code == 401 and not structured:
|
|
1849
|
+
message = "Invalid API key"
|
|
1850
|
+
code = "invalid_api_key"
|
|
1851
|
+
elif status_code == 429 and not structured:
|
|
1852
|
+
message = f"Rate limit exceeded: {message or 'Upgrade at viewspec.dev'}"
|
|
1853
|
+
code = "rate_limit_exceeded"
|
|
1854
|
+
elif not message:
|
|
1855
|
+
message = f"Compilation failed (HTTP {status_code})"
|
|
1856
|
+
request_id = data.get("request_id") if isinstance(data, dict) and isinstance(data.get("request_id"), str) else None
|
|
1857
|
+
request_id = request_id or _response_header(response, "X-Request-ID")
|
|
1858
|
+
return CompilerAPIError(
|
|
1859
|
+
message,
|
|
1860
|
+
code=code,
|
|
1861
|
+
path=path,
|
|
1862
|
+
status_code=status_code,
|
|
1863
|
+
request_id=request_id,
|
|
1864
|
+
retry_after=_response_header(response, "Retry-After"),
|
|
1865
|
+
)
|
|
1808
1866
|
|
|
1809
1867
|
|
|
1810
1868
|
def _compile_request_payload(request: IntentBundle | CompileRequestPayload) -> CompileRequestPayload:
|
|
@@ -1859,7 +1917,9 @@ def compile_remote_response(
|
|
|
1859
1917
|
timeout=30.0,
|
|
1860
1918
|
)
|
|
1861
1919
|
except httpx.HTTPError as exc:
|
|
1862
|
-
raise CompilerAPIError(
|
|
1920
|
+
raise CompilerAPIError(
|
|
1921
|
+
f"Remote compilation request failed: {exc}", code="network_error"
|
|
1922
|
+
) from exc
|
|
1863
1923
|
|
|
1864
1924
|
def _response_json() -> dict[str, Any]:
|
|
1865
1925
|
try:
|
|
@@ -1870,17 +1930,8 @@ def compile_remote_response(
|
|
|
1870
1930
|
raise CompilerAPIError("Compilation failed: response JSON was not an object")
|
|
1871
1931
|
return data
|
|
1872
1932
|
|
|
1873
|
-
if response.status_code == 429:
|
|
1874
|
-
data = _response_json()
|
|
1875
|
-
raise CompilerAPIError(
|
|
1876
|
-
f"Rate limit exceeded: {data.get('message', 'Upgrade at viewspec.dev')}"
|
|
1877
|
-
)
|
|
1878
|
-
if response.status_code == 401:
|
|
1879
|
-
raise CompilerAPIError("Invalid API key")
|
|
1880
1933
|
if response.status_code != 200:
|
|
1881
|
-
raise
|
|
1882
|
-
f"Compilation failed (HTTP {response.status_code}): {response.text}"
|
|
1883
|
-
)
|
|
1934
|
+
raise _compiler_api_error(response, _response_json())
|
|
1884
1935
|
|
|
1885
1936
|
data = _response_json()
|
|
1886
1937
|
if "ast" not in data:
|