norn-cli 0.8.2__tar.gz → 0.8.3__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.
Files changed (52) hide show
  1. {norn_cli-0.8.2/src/norn_cli.egg-info → norn_cli-0.8.3}/PKG-INFO +48 -3
  2. {norn_cli-0.8.2 → norn_cli-0.8.3}/README.md +47 -2
  3. {norn_cli-0.8.2 → norn_cli-0.8.3}/pyproject.toml +1 -1
  4. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/__init__.py +1 -1
  5. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/cli.py +71 -0
  6. norn_cli-0.8.3/src/norf_harness/e2e_scaffold.py +710 -0
  7. norn_cli-0.8.3/src/norf_harness/implementation_artifact.py +188 -0
  8. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/pipeline.py +155 -64
  9. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/grok.py +101 -26
  10. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/SKILL.md +17 -2
  11. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/references/cli.md +19 -2
  12. {norn_cli-0.8.2 → norn_cli-0.8.3/src/norn_cli.egg-info}/PKG-INFO +48 -3
  13. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/SOURCES.txt +3 -0
  14. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_cli.py +1 -1
  15. norn_cli-0.8.3/tests/test_e2e_scaffold.py +331 -0
  16. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_pipeline.py +224 -39
  17. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_providers.py +113 -7
  18. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_skill_install.py +8 -0
  19. {norn_cli-0.8.2 → norn_cli-0.8.3}/LICENSE +0 -0
  20. {norn_cli-0.8.2 → norn_cli-0.8.3}/setup.cfg +0 -0
  21. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/config.py +0 -0
  22. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/doctor.py +0 -0
  23. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/e2e_evidence.py +0 -0
  24. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/gate.py +0 -0
  25. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/models.py +0 -0
  26. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/project_gates.py +0 -0
  27. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/__init__.py +0 -0
  28. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/base.py +0 -0
  29. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/claude.py +0 -0
  30. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/codex.py +0 -0
  31. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/command.py +0 -0
  32. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/registry.py +0 -0
  33. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/router.py +0 -0
  34. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/reporting.py +0 -0
  35. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/default.yaml +0 -0
  36. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/e2e_forward_command.py +0 -0
  37. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/agents/openai.yaml +0 -0
  38. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/run_store.py +0 -0
  39. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/skill_install.py +0 -0
  40. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/tcp_forward.py +0 -0
  41. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/worktree.py +0 -0
  42. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/dependency_links.txt +0 -0
  43. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/entry_points.txt +0 -0
  44. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/requires.txt +0 -0
  45. {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/top_level.txt +0 -0
  46. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_config.py +0 -0
  47. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_e2e_evidence.py +0 -0
  48. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_gate.py +0 -0
  49. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_project_gates.py +0 -0
  50. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_run_store.py +0 -0
  51. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_tcp_forward.py +0 -0
  52. {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_worktree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: norn-cli
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Norn: a provider-neutral multi-agent development harness
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/uruca-kk/norn
@@ -139,6 +139,43 @@ removed when testing ends.
139
139
  For distributable projects, prefer a reviewed, repository-owned E2E command over
140
140
  granting a model browser or host permissions:
141
141
 
142
+ ```bash
143
+ # Adapt the one detected Playwright/Cypress suite. This is a dry run.
144
+ norn init-e2e --adapter existing --project /path/to/repository
145
+
146
+ # Or scaffold a new Playwright entrypoint in a selected workspace package.
147
+ norn init-e2e --preset playwright --package packages/web --project /path/to/repository
148
+ ```
149
+
150
+ `init-e2e` detects npm, pnpm, yarn, or bun and common E2E scripts/configs. It prints
151
+ the files, minimal Norn YAML, evidence limits, and an optional `git.worktree_setup`
152
+ copy step without changing the repository. Add `--write` after review. Use
153
+ `--external-server` for an already-running Docker/external server, or
154
+ `--server-script NAME` when the project script is not `dev`, `start`, or `serve`.
155
+ `--base-url` must be a loopback HTTP(S) URL.
156
+
157
+ The existing-suite adapter generates a repository-owned runner and, for Playwright,
158
+ a small config that extends the existing config while disabling its `webServer` so
159
+ Norn remains the only server owner. The Playwright preset additionally creates a
160
+ bounded evidence helper, a smoke spec, a JSON reporter, and low-volume artifact
161
+ defaults. It does not install Playwright unless `--install-dependency` is explicitly
162
+ combined with `--write`; existing framework versions and fixtures remain owned by
163
+ the project.
164
+
165
+ Re-running `--write` is idempotent. Existing generated files, package scripts, or
166
+ Norn config values are not replaced when they differ; `--force` is the explicit
167
+ migration path. Validate the checked-in contract without launching a browser:
168
+
169
+ ```bash
170
+ norn init-e2e --adapter existing --external-server --check --project /path/to/repository
171
+ norn init-e2e --preset playwright --package packages/web --check --project /path/to/repository
172
+ ```
173
+
174
+ The generated runner receives `NORN_E2E_BASE_URL` and
175
+ `NORN_E2E_EVIDENCE_DIR`, writes its own result record, and keeps evidence within
176
+ the 20-file / 64 MiB limit. Its corresponding configuration is a shell-free argv
177
+ list:
178
+
142
179
  ```yaml
143
180
  e2e:
144
181
  require_all_testers: false
@@ -153,13 +190,21 @@ Norn runs the four static tester roles in parallel, starts or checks the one sha
153
190
  server, then executes `runner.command` exactly once as a shell-free argv list.
154
191
  The implementer may add or update E2E specs but is explicitly told not to launch a
155
192
  browser, start the server, or execute this runner itself.
193
+ The implementer writes its detailed outcome as a run-scoped Markdown artifact and
194
+ returns only a small structured control object containing `status` and the assigned
195
+ artifact path. Norn rejects missing, empty, stale, symlinked, hard-linked, oversized,
196
+ or out-of-contract reports before accepting the provider attempt; changed files are
197
+ derived from Git rather than trusted from the model response.
156
198
  The command receives `NORN_E2E_BASE_URL`, `NORN_E2E_EVIDENCE_DIR`, `NORN_RUN_ID`,
157
199
  and `NORN_IMPLEMENTATION_ROUND`. It must write at least one non-empty log,
158
200
  screenshot, trace, or result file below `NORN_E2E_EVIDENCE_DIR`; Norn's own stdout
159
201
  and stderr logs do not count as evidence. Norn owns the authoritative command,
160
202
  timeout, exit code, and evidence validation, then gives those results to
161
- `e2e_tester` for a read-only review with no command tools. The reviewer cannot
162
- change a failed run into a pass. Managed runners cannot be combined with
203
+ `e2e_tester` for a read-only review with no command tools. The reviewer returns
204
+ only its summary and findings; Norn injects the authoritative test and E2E
205
+ records into the run artifact. Likewise, static testers do not echo a fixed E2E
206
+ placeholder or shared gate results in their structured responses. The reviewer
207
+ cannot change a failed run into a pass. Managed runners cannot be combined with
163
208
  `e2e.tcp_forwards` or `--allow-e2e-danger-full-access` because execution occurs in
164
209
  Norn's process, outside a tester provider sandbox.
165
210
 
@@ -114,6 +114,43 @@ removed when testing ends.
114
114
  For distributable projects, prefer a reviewed, repository-owned E2E command over
115
115
  granting a model browser or host permissions:
116
116
 
117
+ ```bash
118
+ # Adapt the one detected Playwright/Cypress suite. This is a dry run.
119
+ norn init-e2e --adapter existing --project /path/to/repository
120
+
121
+ # Or scaffold a new Playwright entrypoint in a selected workspace package.
122
+ norn init-e2e --preset playwright --package packages/web --project /path/to/repository
123
+ ```
124
+
125
+ `init-e2e` detects npm, pnpm, yarn, or bun and common E2E scripts/configs. It prints
126
+ the files, minimal Norn YAML, evidence limits, and an optional `git.worktree_setup`
127
+ copy step without changing the repository. Add `--write` after review. Use
128
+ `--external-server` for an already-running Docker/external server, or
129
+ `--server-script NAME` when the project script is not `dev`, `start`, or `serve`.
130
+ `--base-url` must be a loopback HTTP(S) URL.
131
+
132
+ The existing-suite adapter generates a repository-owned runner and, for Playwright,
133
+ a small config that extends the existing config while disabling its `webServer` so
134
+ Norn remains the only server owner. The Playwright preset additionally creates a
135
+ bounded evidence helper, a smoke spec, a JSON reporter, and low-volume artifact
136
+ defaults. It does not install Playwright unless `--install-dependency` is explicitly
137
+ combined with `--write`; existing framework versions and fixtures remain owned by
138
+ the project.
139
+
140
+ Re-running `--write` is idempotent. Existing generated files, package scripts, or
141
+ Norn config values are not replaced when they differ; `--force` is the explicit
142
+ migration path. Validate the checked-in contract without launching a browser:
143
+
144
+ ```bash
145
+ norn init-e2e --adapter existing --external-server --check --project /path/to/repository
146
+ norn init-e2e --preset playwright --package packages/web --check --project /path/to/repository
147
+ ```
148
+
149
+ The generated runner receives `NORN_E2E_BASE_URL` and
150
+ `NORN_E2E_EVIDENCE_DIR`, writes its own result record, and keeps evidence within
151
+ the 20-file / 64 MiB limit. Its corresponding configuration is a shell-free argv
152
+ list:
153
+
117
154
  ```yaml
118
155
  e2e:
119
156
  require_all_testers: false
@@ -128,13 +165,21 @@ Norn runs the four static tester roles in parallel, starts or checks the one sha
128
165
  server, then executes `runner.command` exactly once as a shell-free argv list.
129
166
  The implementer may add or update E2E specs but is explicitly told not to launch a
130
167
  browser, start the server, or execute this runner itself.
168
+ The implementer writes its detailed outcome as a run-scoped Markdown artifact and
169
+ returns only a small structured control object containing `status` and the assigned
170
+ artifact path. Norn rejects missing, empty, stale, symlinked, hard-linked, oversized,
171
+ or out-of-contract reports before accepting the provider attempt; changed files are
172
+ derived from Git rather than trusted from the model response.
131
173
  The command receives `NORN_E2E_BASE_URL`, `NORN_E2E_EVIDENCE_DIR`, `NORN_RUN_ID`,
132
174
  and `NORN_IMPLEMENTATION_ROUND`. It must write at least one non-empty log,
133
175
  screenshot, trace, or result file below `NORN_E2E_EVIDENCE_DIR`; Norn's own stdout
134
176
  and stderr logs do not count as evidence. Norn owns the authoritative command,
135
177
  timeout, exit code, and evidence validation, then gives those results to
136
- `e2e_tester` for a read-only review with no command tools. The reviewer cannot
137
- change a failed run into a pass. Managed runners cannot be combined with
178
+ `e2e_tester` for a read-only review with no command tools. The reviewer returns
179
+ only its summary and findings; Norn injects the authoritative test and E2E
180
+ records into the run artifact. Likewise, static testers do not echo a fixed E2E
181
+ placeholder or shared gate results in their structured responses. The reviewer
182
+ cannot change a failed run into a pass. Managed runners cannot be combined with
138
183
  `e2e.tcp_forwards` or `--allow-e2e-danger-full-access` because execution occurs in
139
184
  Norn's process, outside a tester provider sandbox.
140
185
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "norn-cli"
7
- version = "0.8.2"
7
+ version = "0.8.3"
8
8
  description = "Norn: a provider-neutral multi-agent development harness"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -3,4 +3,4 @@
3
3
  from .config import HarnessConfig, load_config
4
4
 
5
5
  __all__ = ["HarnessConfig", "load_config"]
6
- __version__ = "0.8.2"
6
+ __version__ = "0.8.3"
@@ -14,6 +14,13 @@ import yaml
14
14
  from . import __version__
15
15
  from .config import ConfigError, default_config_path, load_config, project_config_path
16
16
  from .doctor import checks_as_dict, doctor_exit_code, run_doctor
17
+ from .e2e_scaffold import (
18
+ E2EScaffoldError,
19
+ apply_e2e_scaffold,
20
+ build_e2e_scaffold,
21
+ check_e2e_scaffold,
22
+ render_e2e_scaffold,
23
+ )
17
24
  from .models import ExitCode, RunMode, RunStatus, StopReason
18
25
  from .pipeline import execute_pipeline
19
26
  from .providers import default_registry
@@ -31,6 +38,23 @@ def build_parser() -> argparse.ArgumentParser:
31
38
  _project_option(init_parser)
32
39
  init_parser.add_argument("--force", action="store_true")
33
40
 
41
+ e2e_init_parser = subparsers.add_parser(
42
+ "init-e2e", help="scaffold a project-owned managed E2E runner"
43
+ )
44
+ _project_option(e2e_init_parser)
45
+ setup_mode = e2e_init_parser.add_mutually_exclusive_group(required=True)
46
+ setup_mode.add_argument("--adapter", choices=("existing",))
47
+ setup_mode.add_argument("--preset", choices=("playwright",))
48
+ e2e_init_parser.add_argument("--package", type=Path)
49
+ e2e_init_parser.add_argument("--base-url", default="http://127.0.0.1:4173")
50
+ e2e_init_parser.add_argument("--server-script")
51
+ e2e_init_parser.add_argument("--external-server", action="store_true")
52
+ action = e2e_init_parser.add_mutually_exclusive_group()
53
+ action.add_argument("--write", action="store_true")
54
+ action.add_argument("--check", action="store_true")
55
+ e2e_init_parser.add_argument("--install-dependency", action="store_true")
56
+ e2e_init_parser.add_argument("--force", action="store_true")
57
+
34
58
  skill_parser = subparsers.add_parser(
35
59
  "install-skill", help="install the bundled Codex and/or Claude skill"
36
60
  )
@@ -112,6 +136,8 @@ def main(argv: list[str] | None = None) -> int:
112
136
  try:
113
137
  if args.command == "init":
114
138
  return _init(args)
139
+ if args.command == "init-e2e":
140
+ return _init_e2e(args)
115
141
  if args.command == "install-skill":
116
142
  return _install_skill(args)
117
143
  config = load_config(
@@ -146,6 +172,9 @@ def main(argv: list[str] | None = None) -> int:
146
172
  except SkillInstallError as exc:
147
173
  print(f"SKILL ERROR: {exc}", file=sys.stderr)
148
174
  return int(ExitCode.CONFIG_ERROR)
175
+ except E2EScaffoldError as exc:
176
+ print(f"E2E SCAFFOLD ERROR: {exc}", file=sys.stderr)
177
+ return int(ExitCode.CONFIG_ERROR)
149
178
  except (RunStoreError, FileNotFoundError) as exc:
150
179
  print(f"RUN ERROR: {exc}", file=sys.stderr)
151
180
  return int(ExitCode.UNEXPECTED_FAILURE)
@@ -181,6 +210,48 @@ def _install_skill(args: argparse.Namespace) -> int:
181
210
  return int(ExitCode.SUCCESS)
182
211
 
183
212
 
213
+ def _init_e2e(args: argparse.Namespace) -> int:
214
+ if args.external_server and args.server_script:
215
+ raise E2EScaffoldError("--external-server cannot be combined with --server-script")
216
+ if args.install_dependency and args.adapter:
217
+ raise E2EScaffoldError("--install-dependency is only available with --preset playwright")
218
+ if args.install_dependency and not args.write:
219
+ raise E2EScaffoldError("--install-dependency requires --write")
220
+ if args.check and args.force:
221
+ raise E2EScaffoldError("--check cannot be combined with --force")
222
+
223
+ plan = build_e2e_scaffold(
224
+ args.project,
225
+ mode=args.adapter or args.preset,
226
+ package=args.package,
227
+ base_url=args.base_url,
228
+ external_server=args.external_server,
229
+ server_script=args.server_script,
230
+ force=args.force,
231
+ )
232
+ if args.check:
233
+ errors = check_e2e_scaffold(plan)
234
+ if errors:
235
+ print("E2E scaffold check failed:", file=sys.stderr)
236
+ for error in errors:
237
+ print(f" - {error}", file=sys.stderr)
238
+ return int(ExitCode.CONFIG_ERROR)
239
+ print("E2E scaffold valid (browser was not launched)")
240
+ return int(ExitCode.SUCCESS)
241
+
242
+ print(render_e2e_scaffold(plan))
243
+ if not args.write:
244
+ print("Dry run only; rerun with --write to create the scaffold.")
245
+ return int(ExitCode.SUCCESS)
246
+
247
+ changed = apply_e2e_scaffold(plan, install_dependency=args.install_dependency)
248
+ if changed:
249
+ print(f"Wrote {len(changed)} file(s).")
250
+ else:
251
+ print("E2E scaffold already up to date.")
252
+ return int(ExitCode.SUCCESS)
253
+
254
+
184
255
  def _ensure_gitignore(root: Path) -> None:
185
256
  path = root / ".gitignore"
186
257
  markers = (".norf/runs/", ".norf/worktrees/", ".norf/e2e-evidence/")