claude-dev-env 1.60.0 → 1.62.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 (41) hide show
  1. package/CLAUDE.md +12 -0
  2. package/audit-rubrics/category_rubrics/category-f-silent-failures.md +1 -1
  3. package/audit-rubrics/prompts/category-e-dead-code.md +17 -4
  4. package/audit-rubrics/prompts/category-f-silent-failures.md +1 -0
  5. package/bin/install.mjs +1 -1
  6. package/docs/CODE_RULES.md +2 -2
  7. package/hooks/blocking/code_rules_annotations_length.py +189 -10
  8. package/hooks/blocking/code_rules_dead_config_field.py +321 -0
  9. package/hooks/blocking/code_rules_enforcer.py +14 -0
  10. package/hooks/blocking/code_rules_orphan_css_class.py +196 -0
  11. package/hooks/blocking/config/verified_commit_constants.py +15 -2
  12. package/hooks/blocking/destructive_command_blocker.py +483 -61
  13. package/hooks/blocking/test_code_rules_enforcer_annotations.py +240 -0
  14. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +1 -0
  15. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +432 -0
  16. package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +82 -0
  17. package/hooks/blocking/test_code_rules_enforcer_orphan_css_class.py +196 -0
  18. package/hooks/blocking/test_destructive_command_blocker.py +213 -0
  19. package/hooks/blocking/test_verification_verdict_store.py +212 -0
  20. package/hooks/blocking/test_verified_commit_gate.py +159 -0
  21. package/hooks/blocking/test_verifier_verdict_minter.py +74 -95
  22. package/hooks/blocking/verification_verdict_store.py +240 -0
  23. package/hooks/blocking/verified_commit_gate.py +31 -9
  24. package/hooks/blocking/verifier_verdict_minter.py +46 -124
  25. package/hooks/hooks_constants/code_rules_enforcer_constants.py +6 -0
  26. package/hooks/hooks_constants/dead_config_field_constants.py +39 -0
  27. package/hooks/hooks_constants/destructive_command_segment_constants.py +15 -0
  28. package/hooks/hooks_constants/orphan_css_class_constants.py +40 -0
  29. package/hooks/validation/mypy_validator.py +59 -7
  30. package/hooks/validation/test_mypy_validator.py +94 -0
  31. package/package.json +1 -1
  32. package/rules/orphan-css-class.md +23 -0
  33. package/skills/autoconverge/reference/gotchas.md +11 -0
  34. package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +5 -1
  35. package/skills/autoconverge/workflow/converge.contract.test.mjs +202 -13
  36. package/skills/autoconverge/workflow/converge.mjs +392 -51
  37. package/skills/autoconverge/workflow/test_render_report.py +55 -0
  38. package/skills/doc-gist/SKILL.md +3 -2
  39. package/skills/doc-gist/references/examples/21-decision-signoff.html +546 -0
  40. package/skills/doc-gist/references/examples/README.md +2 -2
  41. package/skills/task-build/SKILL.md +31 -0
@@ -6,6 +6,7 @@ path the verified_commit_gate hook runs.
6
6
  """
7
7
 
8
8
  import importlib.util
9
+ import json
9
10
  import pathlib
10
11
  import subprocess
11
12
  import sys
@@ -25,6 +26,8 @@ store_spec.loader.exec_module(store_module)
25
26
  is_verification_exempt_diff = store_module.is_verification_exempt_diff
26
27
  resolve_merge_base = store_module.resolve_merge_base
27
28
  branch_surface_manifest = store_module.branch_surface_manifest
29
+ manifest_sha256 = store_module.manifest_sha256
30
+ workflow_verdict_covers_surface = store_module.workflow_verdict_covers_surface
28
31
 
29
32
  constants_spec = importlib.util.spec_from_file_location(
30
33
  "verified_commit_constants",
@@ -276,3 +279,212 @@ def test_production_change_is_gated_on_nonstandard_default_branch(
276
279
  encoding="utf-8",
277
280
  )
278
281
  assert _exemption_for(work_dir) is False
282
+
283
+
284
+ MATCHING_MANIFEST_SHA256 = "a" * 64
285
+ OTHER_MANIFEST_SHA256 = "b" * 64
286
+ VERIFIER_AGENT_TYPE = "code-verifier"
287
+
288
+
289
+ def _verdict_transcript_text(is_all_pass: bool, bound_manifest_sha256: str) -> str:
290
+ verdict_record = {
291
+ "all_pass": is_all_pass,
292
+ "findings": [],
293
+ "manifest_sha256": bound_manifest_sha256,
294
+ }
295
+ assistant_text = (
296
+ "Verification complete.\n\n```verdict\n"
297
+ + json.dumps(verdict_record)
298
+ + "\n```\n"
299
+ )
300
+ assistant_entry = {
301
+ "type": "assistant",
302
+ "message": {"content": [{"type": "text", "text": assistant_text}]},
303
+ }
304
+ return json.dumps(assistant_entry) + "\n"
305
+
306
+
307
+ def _write_agent_transcript(
308
+ subagents_dir: pathlib.Path,
309
+ agent_id: str,
310
+ agent_type: str,
311
+ transcript_text: str,
312
+ should_write_sidecar: bool,
313
+ ) -> None:
314
+ workflow_dir = subagents_dir / "workflows" / "wf_x"
315
+ workflow_dir.mkdir(parents=True, exist_ok=True)
316
+ (workflow_dir / f"agent-{agent_id}.jsonl").write_text(
317
+ transcript_text, encoding="utf-8"
318
+ )
319
+ if should_write_sidecar:
320
+ (workflow_dir / f"agent-{agent_id}.meta.json").write_text(
321
+ json.dumps({"agentType": agent_type}), encoding="utf-8"
322
+ )
323
+
324
+
325
+ def _session_transcript_path(tmp_path: pathlib.Path, session_id: str) -> pathlib.Path:
326
+ session_root = tmp_path / "projects" / "demo"
327
+ session_root.mkdir(parents=True)
328
+ transcript_path = session_root / f"{session_id}.jsonl"
329
+ transcript_path.write_text("", encoding="utf-8")
330
+ return transcript_path
331
+
332
+
333
+ def test_workflow_verdict_covers_surface_true_for_matching_passing_verifier(
334
+ tmp_path: pathlib.Path,
335
+ ) -> None:
336
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
337
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
338
+ _write_agent_transcript(
339
+ subagents_dir,
340
+ "01",
341
+ VERIFIER_AGENT_TYPE,
342
+ _verdict_transcript_text(True, MATCHING_MANIFEST_SHA256),
343
+ should_write_sidecar=True,
344
+ )
345
+ assert (
346
+ workflow_verdict_covers_surface(
347
+ str(transcript_path), MATCHING_MANIFEST_SHA256
348
+ )
349
+ is True
350
+ )
351
+
352
+
353
+ def test_workflow_verdict_covers_surface_false_for_nonmatching_hash(
354
+ tmp_path: pathlib.Path,
355
+ ) -> None:
356
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
357
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
358
+ _write_agent_transcript(
359
+ subagents_dir,
360
+ "01",
361
+ VERIFIER_AGENT_TYPE,
362
+ _verdict_transcript_text(True, OTHER_MANIFEST_SHA256),
363
+ should_write_sidecar=True,
364
+ )
365
+ assert (
366
+ workflow_verdict_covers_surface(
367
+ str(transcript_path), MATCHING_MANIFEST_SHA256
368
+ )
369
+ is False
370
+ )
371
+
372
+
373
+ def test_workflow_verdict_covers_surface_false_for_all_pass_false(
374
+ tmp_path: pathlib.Path,
375
+ ) -> None:
376
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
377
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
378
+ _write_agent_transcript(
379
+ subagents_dir,
380
+ "01",
381
+ VERIFIER_AGENT_TYPE,
382
+ _verdict_transcript_text(False, MATCHING_MANIFEST_SHA256),
383
+ should_write_sidecar=True,
384
+ )
385
+ assert (
386
+ workflow_verdict_covers_surface(
387
+ str(transcript_path), MATCHING_MANIFEST_SHA256
388
+ )
389
+ is False
390
+ )
391
+
392
+
393
+ def test_workflow_verdict_covers_surface_false_for_non_verifier_sidecar(
394
+ tmp_path: pathlib.Path,
395
+ ) -> None:
396
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
397
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
398
+ _write_agent_transcript(
399
+ subagents_dir,
400
+ "01",
401
+ "clean-coder",
402
+ _verdict_transcript_text(True, MATCHING_MANIFEST_SHA256),
403
+ should_write_sidecar=True,
404
+ )
405
+ assert (
406
+ workflow_verdict_covers_surface(
407
+ str(transcript_path), MATCHING_MANIFEST_SHA256
408
+ )
409
+ is False
410
+ )
411
+
412
+
413
+ def test_workflow_verdict_covers_surface_false_for_missing_sidecar(
414
+ tmp_path: pathlib.Path,
415
+ ) -> None:
416
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
417
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
418
+ _write_agent_transcript(
419
+ subagents_dir,
420
+ "01",
421
+ VERIFIER_AGENT_TYPE,
422
+ _verdict_transcript_text(True, MATCHING_MANIFEST_SHA256),
423
+ should_write_sidecar=False,
424
+ )
425
+ assert (
426
+ workflow_verdict_covers_surface(
427
+ str(transcript_path), MATCHING_MANIFEST_SHA256
428
+ )
429
+ is False
430
+ )
431
+
432
+
433
+ def test_workflow_verdict_covers_surface_false_for_missing_subagents_dir(
434
+ tmp_path: pathlib.Path,
435
+ ) -> None:
436
+ transcript_path = _session_transcript_path(tmp_path, "sess1")
437
+ assert (
438
+ workflow_verdict_covers_surface(
439
+ str(transcript_path), MATCHING_MANIFEST_SHA256
440
+ )
441
+ is False
442
+ )
443
+
444
+
445
+ def test_workflow_verdict_covers_surface_true_when_transcript_is_under_subagents(
446
+ tmp_path: pathlib.Path,
447
+ ) -> None:
448
+ subagents_dir = tmp_path / "projects" / "demo" / "sess1" / "subagents"
449
+ _write_agent_transcript(
450
+ subagents_dir,
451
+ "01",
452
+ VERIFIER_AGENT_TYPE,
453
+ _verdict_transcript_text(True, MATCHING_MANIFEST_SHA256),
454
+ should_write_sidecar=True,
455
+ )
456
+ caller_transcript_path = (
457
+ subagents_dir / "workflows" / "wf_x" / "agent-00.jsonl"
458
+ )
459
+ caller_transcript_path.write_text("", encoding="utf-8")
460
+ assert (
461
+ workflow_verdict_covers_surface(
462
+ str(caller_transcript_path), MATCHING_MANIFEST_SHA256
463
+ )
464
+ is True
465
+ )
466
+
467
+
468
+ def test_manifest_hash_cli_prints_live_surface_hash(tmp_path: pathlib.Path) -> None:
469
+ work_dir = _make_repo_with_origin(tmp_path)
470
+ (work_dir / "src" / "app.py").write_text(
471
+ "def add(left: int, right: int) -> int:\n return left - right\n",
472
+ encoding="utf-8",
473
+ )
474
+ merge_base_sha = resolve_merge_base(str(work_dir))
475
+ assert merge_base_sha is not None
476
+ surface_manifest_text = branch_surface_manifest(str(work_dir), merge_base_sha)
477
+ assert surface_manifest_text is not None
478
+ expected_hash = manifest_sha256(surface_manifest_text)
479
+ completed_process = subprocess.run(
480
+ [
481
+ sys.executable,
482
+ str(_HOOK_DIR / "verification_verdict_store.py"),
483
+ "--manifest-hash",
484
+ str(work_dir),
485
+ ],
486
+ check=True,
487
+ capture_output=True,
488
+ text=True,
489
+ )
490
+ assert completed_process.stdout.strip() == expected_hash
@@ -6,8 +6,11 @@ decide what to gate.
6
6
  """
7
7
 
8
8
  import importlib.util
9
+ import io
10
+ import json
9
11
  import os
10
12
  import pathlib
13
+ import subprocess
11
14
  import sys
12
15
 
13
16
  import pytest
@@ -25,6 +28,22 @@ assert gate_spec.loader is not None
25
28
  gate_module = importlib.util.module_from_spec(gate_spec)
26
29
  gate_spec.loader.exec_module(gate_module)
27
30
  gated_repo_directories = gate_module.gated_repo_directories
31
+ deny_reason_for_directory = gate_module.deny_reason_for_directory
32
+ gate_main = gate_module.main
33
+
34
+ store_spec = importlib.util.spec_from_file_location(
35
+ "verification_verdict_store",
36
+ _HOOK_DIR / "verification_verdict_store.py",
37
+ )
38
+ assert store_spec is not None
39
+ assert store_spec.loader is not None
40
+ store_module = importlib.util.module_from_spec(store_spec)
41
+ store_spec.loader.exec_module(store_module)
42
+ resolve_merge_base = store_module.resolve_merge_base
43
+ branch_surface_manifest = store_module.branch_surface_manifest
44
+ manifest_sha256 = store_module.manifest_sha256
45
+
46
+ PRODUCTION_SOURCE = "def add(left: int, right: int) -> int:\n return left + right\n"
28
47
 
29
48
 
30
49
  def test_plain_git_commit_is_gated() -> None:
@@ -366,3 +385,143 @@ def test_git_verb_inside_gh_comment_body_is_not_gated() -> None:
366
385
  assert gated_repo_directories(
367
386
  'gh pr comment -b "please git commit your work"', "/d"
368
387
  ) == []
388
+
389
+
390
+ def _run_git(repo_dir: pathlib.Path, *git_arguments: str) -> None:
391
+ subprocess.run(
392
+ ["git", "-C", str(repo_dir), *git_arguments],
393
+ check=True,
394
+ capture_output=True,
395
+ text=True,
396
+ )
397
+
398
+
399
+ def _make_gated_repo(tmp_path: pathlib.Path) -> pathlib.Path:
400
+ origin_dir = tmp_path / "origin.git"
401
+ work_dir = tmp_path / "work"
402
+ work_dir.mkdir()
403
+ subprocess.run(
404
+ ["git", "init", "--bare", "--initial-branch=main", str(origin_dir)],
405
+ check=True,
406
+ capture_output=True,
407
+ text=True,
408
+ )
409
+ _run_git(work_dir, "init", "--initial-branch=main")
410
+ _run_git(work_dir, "config", "user.email", "tests@example.com")
411
+ _run_git(work_dir, "config", "user.name", "Gate Tests")
412
+ (work_dir / "app.py").write_text(PRODUCTION_SOURCE, encoding="utf-8")
413
+ _run_git(work_dir, "add", "-A")
414
+ _run_git(work_dir, "commit", "-m", "base")
415
+ _run_git(work_dir, "remote", "add", "origin", str(origin_dir))
416
+ _run_git(work_dir, "push", "-u", "origin", "main")
417
+ (work_dir / "app.py").write_text(
418
+ "def add(left: int, right: int) -> int:\n return left - right\n",
419
+ encoding="utf-8",
420
+ )
421
+ return work_dir
422
+
423
+
424
+ def _live_surface_hash(work_dir: pathlib.Path) -> str:
425
+ merge_base_sha = resolve_merge_base(str(work_dir))
426
+ assert merge_base_sha is not None
427
+ surface_manifest_text = branch_surface_manifest(str(work_dir), merge_base_sha)
428
+ assert surface_manifest_text is not None
429
+ return manifest_sha256(surface_manifest_text)
430
+
431
+
432
+ def _write_workflow_verdict(
433
+ transcript_path: pathlib.Path, bound_manifest_sha256: str
434
+ ) -> None:
435
+ subagents_dir = transcript_path.with_suffix("") / "subagents"
436
+ workflow_dir = subagents_dir / "workflows" / "wf_x"
437
+ workflow_dir.mkdir(parents=True)
438
+ verdict_record = {
439
+ "all_pass": True,
440
+ "findings": [],
441
+ "manifest_sha256": bound_manifest_sha256,
442
+ }
443
+ assistant_text = (
444
+ "Verification complete.\n\n```verdict\n"
445
+ + json.dumps(verdict_record)
446
+ + "\n```\n"
447
+ )
448
+ assistant_entry = {
449
+ "type": "assistant",
450
+ "message": {"content": [{"type": "text", "text": assistant_text}]},
451
+ }
452
+ (workflow_dir / "agent-01.jsonl").write_text(
453
+ json.dumps(assistant_entry) + "\n", encoding="utf-8"
454
+ )
455
+ (workflow_dir / "agent-01.meta.json").write_text(
456
+ json.dumps({"agentType": "code-verifier"}), encoding="utf-8"
457
+ )
458
+
459
+
460
+ def _isolate_home(monkeypatch: pytest.MonkeyPatch, fake_home: pathlib.Path) -> None:
461
+ home_text = str(fake_home)
462
+ monkeypatch.setenv("HOME", home_text)
463
+ monkeypatch.setenv("USERPROFILE", home_text)
464
+ monkeypatch.delenv("HOMEDRIVE", raising=False)
465
+ monkeypatch.delenv("HOMEPATH", raising=False)
466
+
467
+
468
+ def test_workflow_verdict_allows_commit_without_a_minted_verdict_file(
469
+ monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
470
+ ) -> None:
471
+ fake_home = tmp_path / "home"
472
+ fake_home.mkdir()
473
+ _isolate_home(monkeypatch, fake_home)
474
+ work_dir = _make_gated_repo(tmp_path)
475
+ live_surface_hash = _live_surface_hash(work_dir)
476
+ transcript_path = tmp_path / "projects" / "demo" / "sess1.jsonl"
477
+ transcript_path.parent.mkdir(parents=True)
478
+ transcript_path.write_text("", encoding="utf-8")
479
+ _write_workflow_verdict(transcript_path, live_surface_hash)
480
+ assert (
481
+ deny_reason_for_directory(str(work_dir), str(transcript_path)) is None
482
+ )
483
+
484
+
485
+ def test_no_verdict_of_either_kind_denies_the_commit(
486
+ monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
487
+ ) -> None:
488
+ fake_home = tmp_path / "home"
489
+ fake_home.mkdir()
490
+ _isolate_home(monkeypatch, fake_home)
491
+ work_dir = _make_gated_repo(tmp_path)
492
+ transcript_path = tmp_path / "projects" / "demo" / "sess1.jsonl"
493
+ transcript_path.parent.mkdir(parents=True)
494
+ transcript_path.write_text("", encoding="utf-8")
495
+ deny_reason = deny_reason_for_directory(str(work_dir), str(transcript_path))
496
+ assert deny_reason is not None
497
+ assert "VERIFIED_COMMIT_GATE" in deny_reason
498
+
499
+
500
+ def _run_gate_main(
501
+ monkeypatch: pytest.MonkeyPatch, command_text: str, work_dir: pathlib.Path
502
+ ) -> None:
503
+ payload_text = json.dumps(
504
+ {
505
+ "tool_name": "Bash",
506
+ "tool_input": {"command": command_text},
507
+ "cwd": str(work_dir),
508
+ "transcript_path": "",
509
+ }
510
+ )
511
+ monkeypatch.setattr(sys, "stdin", io.StringIO(payload_text))
512
+ gate_main()
513
+
514
+
515
+ def test_verification_bypass_marker_allows_an_otherwise_gated_commit(
516
+ monkeypatch: pytest.MonkeyPatch,
517
+ capsys: pytest.CaptureFixture[str],
518
+ tmp_path: pathlib.Path,
519
+ ) -> None:
520
+ fake_home = tmp_path / "home"
521
+ fake_home.mkdir()
522
+ _isolate_home(monkeypatch, fake_home)
523
+ work_dir = _make_gated_repo(tmp_path)
524
+ _run_gate_main(monkeypatch, "git commit -m x", work_dir)
525
+ assert "VERIFIED_COMMIT_GATE" in capsys.readouterr().out
526
+ _run_gate_main(monkeypatch, "git commit -m x # verify-skip", work_dir)
527
+ assert capsys.readouterr().out == ""
@@ -1,18 +1,17 @@
1
1
  """Tests for the agent-type gate in verifier_verdict_minter.
2
2
 
3
- The minter mints a verdict only for a code-verifier stop event. The live
4
- SubagentStop payload names the stopping subagent by ``agent_id`` and carries
5
- no flat agent-type key, so the minter recovers the spawning agent type from
6
- the parent transcript: it walks the parent transcript for the completion
7
- record whose ``agentId`` matches the payload and reads that record's sibling
8
- ``agentType``. These tests build a faithful parent transcript and assert the
9
- minter gates on the resolved type and on the shared MINTING_AGENT_TYPE
10
- constant, so a rename in config propagates to the minter without a second
11
- edit. One test proves that only a structured ``agentType`` key resolves: a
12
- text block that merely quotes the identity keys mints nothing. A further test
13
- holds the shipped settings.json to the minter docstring's anti-forgery claim:
14
- the main session is denied writes to the verdict directory, so only this hook
15
- can mint a passing verdict.
3
+ The minter mints a verdict only for a code-verifier stop event. The
4
+ SubagentStop payload names the stopping subagent's own transcript
5
+ (``agent_transcript_path``), which sits beside a harness-written
6
+ ``agent-<id>.meta.json`` sidecar naming the spawning ``agentType``. These
7
+ tests build that sidecar and assert the minter gates on the resolved type and
8
+ on the shared MINTING_AGENT_TYPE constant, so a rename in config propagates to
9
+ the minter without a second edit. A malformed or non-string sidecar resolves
10
+ nothing, and an absent sidecar mints nothing the main session writes neither
11
+ the transcript nor the sidecar, so it cannot forge a passing verdict. A
12
+ further test holds the shipped settings.json to the minter docstring's
13
+ anti-forgery claim: the main session is denied writes to the verdict
14
+ directory, so only this hook can mint a passing verdict.
16
15
  """
17
16
 
18
17
  import importlib.util
@@ -21,8 +20,6 @@ import pathlib
21
20
  import subprocess
22
21
  import sys
23
22
 
24
- import pytest
25
-
26
23
  _HOOK_DIR = pathlib.Path(__file__).parent
27
24
  if str(_HOOK_DIR) not in sys.path:
28
25
  sys.path.insert(0, str(_HOOK_DIR))
@@ -51,100 +48,83 @@ constants_spec.loader.exec_module(constants_module)
51
48
  MINTING_AGENT_TYPE = constants_module.MINTING_AGENT_TYPE
52
49
 
53
50
 
54
- def _write_parent_transcript(transcript_file: pathlib.Path, agent_id: str, agent_type: str) -> None:
55
- spawn_record = {
56
- "type": "assistant",
57
- "message": {
58
- "content": [
59
- {
60
- "type": "tool_use",
61
- "name": "Task",
62
- "input": {"subagent_type": agent_type, "description": "Verify"},
63
- "agentId": agent_id,
64
- "agentType": agent_type,
65
- "content": [{"type": "text", "text": "verification complete"}],
66
- }
67
- ]
68
- },
69
- }
70
- transcript_file.write_text(json.dumps(spawn_record) + "\n", encoding="utf-8")
51
+ def _write_sidecar(agent_transcript_file: pathlib.Path, agent_type: str) -> None:
52
+ sidecar_file = agent_transcript_file.with_name(f"{agent_transcript_file.stem}.meta.json")
53
+ sidecar_file.write_text(
54
+ json.dumps({"agentType": agent_type, "description": "Verify"}) + "\n",
55
+ encoding="utf-8",
56
+ )
71
57
 
72
58
 
73
- def test_resolves_subagent_type_from_parent_transcript(tmp_path: pathlib.Path) -> None:
74
- transcript_file = tmp_path / "parent.jsonl"
75
- _write_parent_transcript(transcript_file, "agent-7", MINTING_AGENT_TYPE)
76
- payload = {"agent_id": "agent-7", "transcript_path": str(transcript_file)}
59
+ def test_resolves_subagent_type_from_sidecar(tmp_path: pathlib.Path) -> None:
60
+ agent_transcript = tmp_path / "agent-7.jsonl"
61
+ agent_transcript.write_text("", encoding="utf-8")
62
+ _write_sidecar(agent_transcript, MINTING_AGENT_TYPE)
63
+ payload = {"agent_transcript_path": str(agent_transcript)}
77
64
  assert resolved_subagent_type(payload) == MINTING_AGENT_TYPE
78
65
 
79
66
 
80
- def test_resolves_none_when_agent_id_absent_from_transcript(
81
- tmp_path: pathlib.Path,
82
- ) -> None:
83
- transcript_file = tmp_path / "parent.jsonl"
84
- _write_parent_transcript(transcript_file, "agent-7", MINTING_AGENT_TYPE)
85
- payload = {"agent_id": "different-agent", "transcript_path": str(transcript_file)}
67
+ def test_resolves_none_when_sidecar_absent(tmp_path: pathlib.Path) -> None:
68
+ agent_transcript = tmp_path / "agent-7.jsonl"
69
+ agent_transcript.write_text("", encoding="utf-8")
70
+ payload = {"agent_transcript_path": str(agent_transcript)}
86
71
  assert resolved_subagent_type(payload) is None
87
72
 
88
73
 
89
- def test_resolves_type_when_record_arrives_after_first_read(
90
- tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
91
- ) -> None:
92
- transcript_file = tmp_path / "parent.jsonl"
93
- transcript_file.write_text("", encoding="utf-8")
74
+ def test_resolves_none_when_agent_transcript_path_empty() -> None:
75
+ assert resolved_subagent_type({"agent_transcript_path": ""}) is None
76
+ assert resolved_subagent_type({}) is None
94
77
 
95
- def write_record_on_first_sleep(_seconds: float) -> None:
96
- if transcript_file.read_text(encoding="utf-8"):
97
- return
98
- _write_parent_transcript(transcript_file, "agent-7", MINTING_AGENT_TYPE)
99
78
 
100
- monkeypatch.setattr(minter_module.time, "sleep", write_record_on_first_sleep)
101
- payload = {"agent_id": "agent-7", "transcript_path": str(transcript_file)}
102
- assert resolved_subagent_type(payload) == MINTING_AGENT_TYPE
79
+ def test_resolves_none_when_sidecar_names_no_string_type(tmp_path: pathlib.Path) -> None:
80
+ agent_transcript = tmp_path / "agent-7.jsonl"
81
+ agent_transcript.write_text("", encoding="utf-8")
82
+ sidecar_file = agent_transcript.with_name("agent-7.meta.json")
83
+ sidecar_file.write_text(json.dumps({"agentType": 123}), encoding="utf-8")
84
+ payload = {"agent_transcript_path": str(agent_transcript)}
85
+ assert resolved_subagent_type(payload) is None
103
86
 
104
87
 
105
- def test_quoted_agent_type_in_text_block_does_not_resolve(
106
- tmp_path: pathlib.Path,
107
- ) -> None:
108
- transcript_file = tmp_path / "parent.jsonl"
109
- forged_entry = {
110
- "type": "assistant",
111
- "message": {
112
- "content": [
113
- {
114
- "type": "text",
115
- "text": json.dumps({"agentId": "agent-7", "agentType": MINTING_AGENT_TYPE}),
116
- }
117
- ]
118
- },
119
- }
120
- transcript_file.write_text(json.dumps(forged_entry) + "\n", encoding="utf-8")
121
- payload = {"agent_id": "agent-7", "transcript_path": str(transcript_file)}
88
+ def test_unparseable_sidecar_resolves_nothing(tmp_path: pathlib.Path) -> None:
89
+ agent_transcript = tmp_path / "agent-7.jsonl"
90
+ agent_transcript.write_text("", encoding="utf-8")
91
+ sidecar_file = agent_transcript.with_name("agent-7.meta.json")
92
+ sidecar_file.write_text("{not valid json", encoding="utf-8")
93
+ payload = {"agent_transcript_path": str(agent_transcript)}
94
+ assert resolved_subagent_type(payload) is None
95
+
96
+
97
+ def test_invalid_utf8_sidecar_resolves_nothing(tmp_path: pathlib.Path) -> None:
98
+ agent_transcript = tmp_path / "agent-7.jsonl"
99
+ agent_transcript.write_text("", encoding="utf-8")
100
+ sidecar_file = agent_transcript.with_name("agent-7.meta.json")
101
+ sidecar_file.write_bytes(b'{"agentType": "\xff\xfe bad"}')
102
+ payload = {"agent_transcript_path": str(agent_transcript)}
103
+ assert resolved_subagent_type(payload) is None
104
+
105
+
106
+ def test_non_object_json_sidecar_resolves_nothing(tmp_path: pathlib.Path) -> None:
107
+ agent_transcript = tmp_path / "agent-7.jsonl"
108
+ agent_transcript.write_text("", encoding="utf-8")
109
+ sidecar_file = agent_transcript.with_name("agent-7.meta.json")
110
+ sidecar_file.write_text(json.dumps(["agentType", "code-verifier"]), encoding="utf-8")
111
+ payload = {"agent_transcript_path": str(agent_transcript)}
122
112
  assert resolved_subagent_type(payload) is None
123
113
 
124
114
 
125
115
  def test_non_verifier_agent_type_mints_nothing(tmp_path: pathlib.Path) -> None:
126
- transcript_file = tmp_path / "parent.jsonl"
127
- _write_parent_transcript(transcript_file, "agent-7", "general-purpose")
128
- payload = {
129
- "agent_id": "agent-7",
130
- "transcript_path": str(transcript_file),
131
- "agent_transcript_path": "",
132
- "cwd": ".",
133
- }
116
+ agent_transcript = tmp_path / "agent-7.jsonl"
117
+ agent_transcript.write_text("", encoding="utf-8")
118
+ _write_sidecar(agent_transcript, "general-purpose")
119
+ payload = {"agent_transcript_path": str(agent_transcript)}
134
120
  assert mint_for_payload(payload) is None
135
121
 
136
122
 
137
- def test_minting_agent_type_passes_the_agent_type_gate(
138
- tmp_path: pathlib.Path,
139
- ) -> None:
140
- transcript_file = tmp_path / "parent.jsonl"
141
- _write_parent_transcript(transcript_file, "agent-7", MINTING_AGENT_TYPE)
142
- payload = {
143
- "agent_id": "agent-7",
144
- "transcript_path": str(transcript_file),
145
- "agent_transcript_path": "",
146
- "cwd": ".",
147
- }
123
+ def test_verifier_type_without_a_verdict_mints_nothing(tmp_path: pathlib.Path) -> None:
124
+ agent_transcript = tmp_path / "agent-7.jsonl"
125
+ agent_transcript.write_text("", encoding="utf-8")
126
+ _write_sidecar(agent_transcript, MINTING_AGENT_TYPE)
127
+ payload = {"agent_transcript_path": str(agent_transcript)}
148
128
  assert mint_for_payload(payload) is None
149
129
 
150
130
 
@@ -165,9 +145,7 @@ def test_clean_verifier_verdict_mints_a_verdict_file(tmp_path: pathlib.Path) ->
165
145
  repo_root = tmp_path / "repo"
166
146
  repo_root.mkdir()
167
147
  _init_repo_with_upstream_and_edit(repo_root)
168
- transcript_file = tmp_path / "parent.jsonl"
169
- _write_parent_transcript(transcript_file, "agent-7", MINTING_AGENT_TYPE)
170
- agent_transcript = tmp_path / "agent.jsonl"
148
+ agent_transcript = tmp_path / "agent-7.jsonl"
171
149
  agent_transcript.write_text(
172
150
  json.dumps(
173
151
  {
@@ -185,17 +163,18 @@ def test_clean_verifier_verdict_mints_a_verdict_file(tmp_path: pathlib.Path) ->
185
163
  + "\n",
186
164
  encoding="utf-8",
187
165
  )
166
+ _write_sidecar(agent_transcript, MINTING_AGENT_TYPE)
188
167
  payload = {
189
- "agent_id": "agent-7",
190
- "transcript_path": str(transcript_file),
191
168
  "agent_transcript_path": str(agent_transcript),
192
169
  "cwd": str(repo_root),
170
+ "agent_id": "a02b9583eedc74093",
193
171
  }
194
172
  verdict_path = mint_for_payload(payload)
195
173
  try:
196
174
  assert verdict_path is not None
197
175
  verdict_record = json.loads(verdict_path.read_text(encoding="utf-8"))
198
176
  assert verdict_record["all_pass"] is True
177
+ assert verdict_record["minted_from_agent_id"] == "a02b9583eedc74093"
199
178
  finally:
200
179
  if verdict_path is not None and verdict_path.exists():
201
180
  verdict_path.unlink()