claude-dev-env 8.15.0 → 8.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/test_agent_frontmatter.py +0 -72
- package/.agents/agents-archived/code-quality-agent.md +16 -16
- package/.agents/skills/_shared/end-of-run-gotcha-recommendations.md +5 -21
- package/.agents/skills/e-code-review/SKILL.md +1 -3
- package/.agents/skills/e-simplify/SKILL.md +6 -6
- package/.agents/skills/eli5/SKILL.md +4 -14
- package/.agents/skills/eli5/reference/task-seeds.md +3 -4
- package/.agents/skills/everything-search/SKILL.md +2 -2
- package/.agents/skills/fresh-branch/SKILL.md +2 -3
- package/.agents/skills/grok-spawn/SKILL.md +3 -8
- package/.agents/skills/issue-tracker/SKILL.md +4 -6
- package/.agents/skills/orchestrator-refresh/SKILL.md +7 -7
- package/.agents/skills/plugin-eval-standalone-skill/SKILL.md +3 -3
- package/.agents/skills/privacy-hygiene/SKILL.md +6 -10
- package/.agents/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
- package/.agents/skills/pull-request/SKILL.md +11 -14
- package/.agents/skills/pull-request/reference/publication-tasks.md +2 -4
- package/.agents/skills/recovering-codex-startup/SKILL.md +3 -6
- package/.agents/skills/recovering-codex-startup/reference/task-seeds.md +1 -1
- package/.agents/skills/repairing-hook-boundaries/SKILL.md +4 -7
- package/.agents/skills/repairing-hook-boundaries/reference/task-seeds.md +1 -2
- package/.agents/skills/syncing-submodules/SKILL.md +7 -17
- package/.agents/skills/syncing-submodules/reference/run-tasks.md +1 -1
- package/.agents/skills/task-build/SKILL.md +1 -1
- package/.agents/skills/task-build/reference/tool-routing.md +1 -1
- package/.agents/skills/team-advisor/SKILL.md +2 -4
- package/.agents/skills/usage-pause/SKILL.md +1 -1
- package/.agents/skills/windows-scheduled-task/SKILL.md +1 -1
- package/AGENTS.md +1 -1
- package/_shared/advisor/advisor-protocol.md +4 -4
- package/_shared/advisor/reference/consult-format.md +1 -1
- package/_shared/advisor/reference/identity.md +2 -2
- package/_shared/advisor/reference/warm-up.md +1 -1
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -16
- package/bin/install.mjs +1 -0
- package/bin/install.prompt-context.test.mjs +7 -3
- package/bin/install.test.mjs +3 -2
- package/commands/sr-loop.md +2 -2
- package/docs/CODE_RULES.md +2 -2
- package/docs/DJANGO_PATTERNS.md +3 -3
- package/docs/REACT_PATTERNS.md +6 -6
- package/docs/TEST_QUALITY.md +10 -16
- package/docs/agent-spawn-protocol.md +4 -4
- package/docs/codex-compatibility.md +4 -33
- package/docs/references/team-advisor-skill.md +0 -1
- package/docs/worker-completion-gate.md +0 -1
- package/hooks/advisory/migration_safety_advisor.py +3 -2
- package/hooks/advisory/refactor_guard.py +3 -3
- package/hooks/hooks.json +1 -1
- package/hooks/hooks_constants/issue_tracker_session_starter_constants.py +1 -2
- package/hooks/hooks_constants/orchestrator_auto_starter_constants.py +1 -2
- package/package.json +1 -1
- package/rules/explore-thoroughly.md +0 -1
- package/rules/filesystem-search.md +1 -1
- package/rules/git-workflow.md +5 -5
- package/rules/long-horizon-autonomy.md +1 -5
- package/rules/re-stage-before-commit.md +0 -2
- package/rules/shell-invocation.md +1 -1
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +8 -2
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/policy_lint/engine.py +5 -2
- package/scripts/repository_checks/config/constants.py +1 -1
- package/scripts/spawn_grok_batch.py +12 -1
- package/scripts/test_spawn_grok_batch.py +22 -0
- package/scripts/tests/test_banned_prose_words.py +1 -1
- package/scripts/tests/test_policy_lint_rules_archive.py +16 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +2 -2
- package/scripts/tests/test_rules.py +9 -9
- package/system-prompts/software-engineer.xml +15 -30
- package/audit-rubrics/audit-categories.json +0 -700
- package/audit-rubrics/category_rubrics/category-a-api-contracts.md +0 -90
- package/audit-rubrics/category_rubrics/category-b-selector-engine-compat.md +0 -36
- package/audit-rubrics/category_rubrics/category-c-resource-cleanup.md +0 -35
- package/audit-rubrics/category_rubrics/category-d-scoping-and-ordering.md +0 -35
- package/audit-rubrics/category_rubrics/category-e-dead-code.md +0 -40
- package/audit-rubrics/category_rubrics/category-f-silent-failures.md +0 -41
- package/audit-rubrics/category_rubrics/category-g-bounds-and-overflow.md +0 -38
- package/audit-rubrics/category_rubrics/category-h-security-boundaries.md +0 -40
- package/audit-rubrics/category_rubrics/category-i-concurrency.md +0 -38
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +0 -59
- package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +0 -66
- package/audit-rubrics/category_rubrics/category-l-behavior-equivalence.md +0 -48
- package/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md +0 -44
- package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +0 -54
- package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +0 -59
- package/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md +0 -46
- package/audit-rubrics/prompts/category-a-api-contracts.md +0 -407
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +0 -401
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +0 -420
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +0 -414
- package/audit-rubrics/prompts/category-e-dead-code.md +0 -434
- package/audit-rubrics/prompts/category-f-silent-failures.md +0 -432
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +0 -383
- package/audit-rubrics/prompts/category-h-security-boundaries.md +0 -423
- package/audit-rubrics/prompts/category-i-concurrency.md +0 -429
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +0 -458
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +0 -338
- package/audit-rubrics/prompts/category-l-behavior-equivalence.md +0 -131
- package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +0 -129
- package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +0 -139
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +0 -89
- package/audit-rubrics/prompts/category-q-cross-surface-claims.md +0 -51
- package/hooks/hooks_constants/style_reminder_prompt_constants.py +0 -9
- package/hooks/session/style_reminder_prompt.py +0 -55
- package/hooks/session/test_style_reminder_prompt.py +0 -86
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
Audit [REPO/ARTIFACT] [TARGET_ID] for **Category N only** (test-name scenario verifier). Skip A–M, O, P. Sub-bucket forced-exhaustion mode: Category N is decomposed into 10 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
2
|
-
|
|
3
|
-
[ARTIFACT METADATA — include every changed test alongside the production code path it claims to cover]
|
|
4
|
-
|
|
5
|
-
- Title / one-line summary: [TITLE]
|
|
6
|
-
- Head ref / SHA at audit time: [HEAD_SHA]
|
|
7
|
-
- Changed test functions (file + line range + test name + first-line assertion): [CHANGED_TESTS]
|
|
8
|
-
- Production functions the tests claim to cover (file + line range + symbol name + branch structure): [PRODUCTION_TARGETS]
|
|
9
|
-
- Scenario fixtures / monkeypatches in scope (`monkeypatch.setattr`, `pytest.mark.skipif`, `freezegun.freeze_time`, `mock.patch`): [SCENARIO_GATES]
|
|
10
|
-
- Stated intent of each scenario-named test (what condition the test name claims to exercise): [INTENT]
|
|
11
|
-
|
|
12
|
-
ID prefix: `find`.
|
|
13
|
-
|
|
14
|
-
[ONE-PARAGRAPH FRAME: enumerate every test whose name includes a scenario claim (`_when_*`, `_at_*`, `_under_*`, `_with_*`, `_on_*`, `_after_*`, `_during_*`). State the audit goal: for each scenario-named test, verify the body sets up the named condition via fixture / monkeypatch / environment gate so the production code's scenario-named branch runs during the act phase.]
|
|
15
|
-
|
|
16
|
-
## Source material ([N] files/sections, all lines in scope)
|
|
17
|
-
|
|
18
|
-
[INLINE every changed test function alongside the production function it claims to cover. Include the production function's branch structure so the audit can identify the no-op / early-return / default branches that scenario-named tests must NOT silently pass against.]
|
|
19
|
-
|
|
20
|
-
## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
|
|
21
|
-
|
|
22
|
-
**N1. Scenario-named tests demonstrate the scenario** ⭐ canonical N case
|
|
23
|
-
- For every test whose name contains `_when_X` / `_at_X` / `_under_X` / `_with_X` / `_on_X` / `_after_X` / `_during_X`, verify the body sets up condition X via fixture, monkeypatch, or environment gate before calling the system under test.
|
|
24
|
-
- Adversarial probes: (a) construct an input that satisfies the test's assertion but does NOT trigger the scenario-named code path — does the test still pass; (b) trace the production function's code path under the test's input — which branch executes during the act phase; (c) inspect the test's setup-phase for monkeypatch / fixture calls that gate the scenario.
|
|
25
|
-
|
|
26
|
-
**N2. Path-decision parametric matrices**
|
|
27
|
-
- For tests of `is_*_path` / `_resolve_*_path` / `*_path_exemptions` modules, verify the test corpus ships a parametric matrix covering: empty string, single filename, tilde-prefix, UNC path, drive-letter path, symlinked path, `..`-containing path, trailing-slash path.
|
|
28
|
-
- Adversarial probes: (a) walk the production function's path-classification branches — which branch does each input class hit; (b) check the test corpus for input shapes that hit only the default / no-classification branch; (c) for each input class missing from the matrix, construct a probe input and trace which branch executes.
|
|
29
|
-
|
|
30
|
-
**N3. Tests that pass "for the wrong reason"**
|
|
31
|
-
- For every assertion of the shape `assert <substring> in result`, verify the substring shape is unique to the scenario-named branch's output.
|
|
32
|
-
- Adversarial probe: walk the production function's branches; for each branch, build the output and test the substring against it. If the substring matches more than one branch's output, the assertion cannot discriminate which branch ran.
|
|
33
|
-
|
|
34
|
-
**N4. No-op branch exercised by scenario name**
|
|
35
|
-
- For every scenario-named test, identify the production function's no-op / early-return / no-feature-installed branch. Verify the test's constructed input does NOT hit that branch.
|
|
36
|
-
- Adversarial probes: (a) any test whose input fails the production function's first guard returns the no-op default and the assertion checks the default; (b) any test whose input is empty / None / missing returns early; (c) any test whose fixture is not installed at the test runtime hits the "feature missing" branch.
|
|
37
|
-
|
|
38
|
-
**N5. Assertion shape mismatch**
|
|
39
|
-
- For every assertion, verify the assertion's shape can fail by construction. `assert <substring> not in result` where the substring is misspelled relative to the production output, or `assert result == ""` when the production function returns `None` on the negative case, or `len(result) > 0` when the production function returns an empty list on the no-feature path.
|
|
40
|
-
- Adversarial probes: (a) inspect each assertion's shape against the production function's return-value space; (b) check for assertions where the substring shape never appears in the production output by construction; (c) check for `assert x is True` where the production function returns truthy non-bool values.
|
|
41
|
-
|
|
42
|
-
**N6. Cross-platform scenario gating**
|
|
43
|
-
- For every test named `_on_windows` / `_on_linux` / `_on_macos`, verify the body gates on `sys.platform`, `monkeypatch.setattr(os, "name", ...)`, or `@pytest.mark.skipif`.
|
|
44
|
-
- Bare scenario names that run unchanged across platforms claim more than they prove.
|
|
45
|
-
- Adversarial probes: (a) does the production function's platform-specific branch get skipped on the CI runner's platform; (b) does the test pass against the platform fallback rather than the platform-specific code; (c) is the platform fixture installed and respected by the test runner.
|
|
46
|
-
|
|
47
|
-
**N7. Time / clock scenario gating**
|
|
48
|
-
- For every test named `_after_<duration>` / `_at_midnight` / `_during_business_hours`, verify the body injects a frozen clock (`freezegun.freeze_time`, `monkeypatch.setattr(time, "time", ...)`, `unittest.mock.patch("datetime.now")`).
|
|
49
|
-
- Wall-clock tests are non-deterministic and may pass against the wrong scenario.
|
|
50
|
-
- Adversarial probes: (a) does the test's act phase depend on the system clock being at a specific value; (b) does any timezone shift cause the test to flake; (c) does the production function read the clock during the act phase.
|
|
51
|
-
|
|
52
|
-
**N8. Concurrent / load scenario gating**
|
|
53
|
-
- For every test named `_under_load` / `_with_concurrent_writers` / `_under_contention`, verify the body spawns the concurrent workers and `wait()`s on them.
|
|
54
|
-
- Single-threaded tests cannot claim concurrent-scenario coverage.
|
|
55
|
-
- Adversarial probes: (a) does the test spawn `threading.Thread` / `multiprocessing.Process` / `asyncio.gather` / `concurrent.futures.ThreadPoolExecutor`; (b) does the test's act phase exercise the concurrency primitive the production function relies on; (c) does the test introduce a race window the production function's lock should serialize.
|
|
56
|
-
|
|
57
|
-
**N9. Neutral-named tests (out of scope)**
|
|
58
|
-
- Tests named `test_returns_empty_list_for_unknown_key` / `test_handles_y` / `test_raises_value_error` (no scenario claim in the name) are NOT subject to N1–N8.
|
|
59
|
-
- For neutral-named tests, only N5 (assertion shape mismatch) applies.
|
|
60
|
-
|
|
61
|
-
**N10. Test fixture wiring correctness**
|
|
62
|
-
- For every test, verify the fixture / path / import wiring resolves to the artifact the test name claims.
|
|
63
|
-
- Path arithmetic: walk every `Path(__file__).parents[k]` chain symbolically and confirm it reaches the directory the assertion expects — a `parents[3]` that stops at `skills/` while the test expects the package root cannot fail for the right reason.
|
|
64
|
-
- Same-symbol dual imports: `from module import helper` plus `from module import helper as helper_alias` bind two names to the same function object, so any parity assertion between the two bound names is true by construction and proves nothing.
|
|
65
|
-
- Fixture file lookups: confirm every `open(Path(__file__).parent / "fixture.txt")` (or equivalent) reaches a file that exists in the repo.
|
|
66
|
-
- Adversarial probes: (a) re-derive each `parents[k]` index against the directory depth and flag any off-by-k; (b) check whether two imports in the test resolve to the same object before trusting a cross-name comparison; (c) confirm each referenced fixture path exists on disk at the depth the arithmetic produces.
|
|
67
|
-
|
|
68
|
-
## Cross-bucket questions to answer at the end
|
|
69
|
-
|
|
70
|
-
Q1: Across all 10 sub-buckets, is there a scenario-named test that does not exercise the named scenario? Cite the test's file:line and the production function's scenario-named branch that should have been exercised.
|
|
71
|
-
|
|
72
|
-
Q2: What's the worst false-coverage signal introduced by the diff? Evaluate by (a) whether the test's name is load-bearing in the suite's coverage report, (b) whether the named scenario has any other coverage; (c) whether removing the test would change the coverage percentage.
|
|
73
|
-
|
|
74
|
-
Q3: Which scenario-named test most likely will start passing for the wrong reason in a future refactor? Identify tests whose assertions match substrings that could appear in multiple branches — these are time bombs.
|
|
75
|
-
|
|
76
|
-
## Output
|
|
77
|
-
|
|
78
|
-
Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket N1-N10, produce Shape A or Shape B (with ≥3 probes). Each Shape A finding must cite the test's file:line AND the production function's branch the test's name claims to cover. Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 scenario-named tests that exercise the no-op branch — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
# Worked example: jl-cmd/claude-dev-env PR #476
|
|
83
|
-
|
|
84
|
-
Audit jl-cmd/claude-dev-env PR #476 for **Category N only** (test-name scenario verifier). Skip A–M. Sub-bucket forced-exhaustion mode: Category N is decomposed into 9 sub-buckets below.
|
|
85
|
-
|
|
86
|
-
PR: refactor(hooks): cross-platform path resolution for windows-rmtree-blocker
|
|
87
|
-
Head SHA: (the commit that landed the platform-conditional logic)
|
|
88
|
-
ID prefix: `find`.
|
|
89
|
-
|
|
90
|
-
The PR adds platform-conditional path-resolution logic to `windows_rmtree_blocker.py` and ships 5 new tests named `test_*_on_windows` and `test_*_on_linux` across `test_windows_rmtree_blocker.py`. The audit goal: verify each scenario-named test sets up the named platform via monkeypatch or skipif gate so the production function's platform-specific branch runs during the act phase.
|
|
91
|
-
|
|
92
|
-
## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
|
|
93
|
-
|
|
94
|
-
**N1. Scenario-named tests demonstrate the scenario** ⭐ canonical N case — Shape A findings F5, F21, F23, F26, F27
|
|
95
|
-
- `test_resolves_path_on_windows` calls `windows_rmtree_blocker.resolve_path("C:/Users/test")` and asserts the result equals `Path("C:/Users/test")`. The body does NOT call `monkeypatch.setattr(sys, "platform", "win32")` or `@pytest.mark.skipif(sys.platform != "win32")`. On a Linux CI runner, `sys.platform == "linux"` is in effect when the test runs; the production function's `if sys.platform == "win32":` branch is skipped, and the assertion succeeds against the Linux fallback branch's output (which happens to match `Path("C:/Users/test")` because `pathlib.PurePath` accepts Windows-style strings on Linux without normalization).
|
|
96
|
-
- The test's NAME claims Windows-branch coverage; the test's BODY exercises the Linux fallback. This is the canonical N1 finding shape.
|
|
97
|
-
- Adversarial probe (a): construct an input that the Windows branch would handle differently from the Linux branch — does the test catch the divergence? In F5's case, no: the assertion uses a string that both branches happen to produce, so the test cannot discriminate.
|
|
98
|
-
- Adversarial probe (b): the production function's `sys.platform == "win32"` branch performs UNC-prefix stripping; the Linux fallback does not. Inputs containing `\\?\` would yield different outputs on the two branches. The test does not use such inputs.
|
|
99
|
-
- Adversarial probe (c): the test runtime's `sys.platform` is `"linux"` on the CI runner. The act phase hits the fallback, full stop.
|
|
100
|
-
- **Severity P1** for each of F5, F21, F23, F26, F27: scenario-named tests claim platform-specific coverage they do not provide.
|
|
101
|
-
- **Fix**: wrap each `_on_windows`-named test in `@pytest.mark.skipif(sys.platform != "win32", reason="windows-specific path resolution")` AND duplicate as `_on_linux` for the Linux fallback branch; OR use `monkeypatch.setattr(sys, "platform", "win32")` to force the named platform during the act phase.
|
|
102
|
-
|
|
103
|
-
**N2. Path-decision parametric matrices**
|
|
104
|
-
- The production function `resolve_path` is a path-classifier — it qualifies for N2 coverage. The PR ships 5 inputs: drive-letter, UNC-prefix, tilde-prefix, `..`-containing, and trailing-slash. Missing: empty string, single filename, symlinked path. These three input classes have no test in the diff.
|
|
105
|
-
- Adversarial probes: (a) construct an empty-string input — does any branch handle it; (b) construct a single-filename input (no directory component) — does the function return as-is or attempt to resolve against cwd; (c) construct a symlinked path — does the function resolve through the symlink or preserve it.
|
|
106
|
-
|
|
107
|
-
**N3. Tests that pass "for the wrong reason"**
|
|
108
|
-
- See N1 findings F5, F21, F23, F26, F27 — each passes because the assertion's substring matches both the Windows-branch output and the Linux-fallback output. The assertion shape cannot discriminate which branch ran.
|
|
109
|
-
|
|
110
|
-
**N4. No-op branch exercised by scenario name**
|
|
111
|
-
- F5 finding above: the scenario-named test exercises the Linux-fallback no-op branch on the CI runner.
|
|
112
|
-
|
|
113
|
-
**N5. Assertion shape mismatch**
|
|
114
|
-
- All five tests use `assert result == Path(<expected>)`. The shape can fail by construction (Path equality is strict). N5 verified clean.
|
|
115
|
-
|
|
116
|
-
**N6. Cross-platform scenario gating** ⭐
|
|
117
|
-
- Five `_on_windows`-named tests have zero platform gating. Five `_on_linux`-named tests have zero platform gating. N6 is the structural lens on the N1 findings — every test's NAME claims platform coverage, every test's BODY ignores the platform gate.
|
|
118
|
-
- See N1 F5 / F21 / F23 / F26 / F27.
|
|
119
|
-
|
|
120
|
-
**N7. Time / clock scenario gating**
|
|
121
|
-
- No time-named tests in scope. N7 verified clean.
|
|
122
|
-
|
|
123
|
-
**N8. Concurrent / load scenario gating**
|
|
124
|
-
- No concurrency-named tests in scope. N8 verified clean.
|
|
125
|
-
|
|
126
|
-
**N9. Neutral-named tests (out of scope)**
|
|
127
|
-
- One test in the diff is neutrally named (`test_returns_path_unchanged_when_already_absolute`). N9 marks it out of scope for N1-N4 / N6-N8; only N5 applies. The assertion is `assert result == input_path` — shape clean. Verified clean.
|
|
128
|
-
|
|
129
|
-
## Cross-bucket questions to answer at the end
|
|
130
|
-
|
|
131
|
-
Q1: Five scenario-named tests (F5, F21, F23, F26, F27) do not gate on `sys.platform` and pass against the Linux-fallback branch on the CI runner. The Windows-specific code path has zero coverage despite the test names claiming it. Cite `test_windows_rmtree_blocker.py:42` (F5 first test) and `windows_rmtree_blocker.py:67` (the `if sys.platform == "win32":` branch) as the misclaim pair.
|
|
132
|
-
|
|
133
|
-
Q2: Worst false-coverage signal: F5 — the test's name `test_resolves_path_on_windows` reads as Windows-branch coverage in the PR review, but the act phase exercises the Linux fallback. A reviewer reading the test name during PR review would assume Windows coverage exists; it does not.
|
|
134
|
-
|
|
135
|
-
Q3: Once the Windows branch and the Linux branch diverge in their output for the same input — for example, a future PR that adds normalization to the Windows branch only — these five tests will start failing on Windows CI, exposing the false coverage retroactively.
|
|
136
|
-
|
|
137
|
-
## Output
|
|
138
|
-
|
|
139
|
-
Lead: `Total: 5 (P0=0, P1=5, P2=0)`. F5, F21, F23, F26, F27 are the N1+N6 scenario-gate-missing findings. N2 has one finding (parametric matrix incomplete) at P2. N3 / N4 are subsumed by N1. N5 / N7 / N8 / N9 verified clean. Adversarial second pass: scan for any non-`_on_<platform>`-named test that exercises the platform-conditional branch — verified none in this diff. Open Questions: whether the PR author intended any of the `_on_<platform>` tests to be platform-gated; resolve via reply on the audit thread. Read-only. No edits, no commits.
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
Audit [REPO/ARTIFACT] [TARGET_ID] for **Category P only** (name / regex / word-list vs behavior-contract precision). Skip A–O. Sub-bucket forced-exhaustion mode: Category P is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
2
|
-
|
|
3
|
-
[ARTIFACT METADATA — every newly-added or renamed identifier plus the body code that implements its contract]
|
|
4
|
-
|
|
5
|
-
- Title / one-line summary: [TITLE]
|
|
6
|
-
- Head ref / SHA at audit time: [HEAD_SHA]
|
|
7
|
-
- New / renamed boolean flags (file:line + identifier + lifecycle sites): [NEW_FLAGS]
|
|
8
|
-
- New / renamed predicate helpers (file:line + identifier + body + return contract): [NEW_PREDICATES]
|
|
9
|
-
- New / renamed regex constants (file:line + identifier + regex source + anchors): [NEW_REGEXES]
|
|
10
|
-
- New / renamed word-lists or replacement tables (file:line + identifier + entries): [NEW_LISTS]
|
|
11
|
-
- New / renamed helper-function names (file:line + identifier + signature + return): [NEW_HELPERS]
|
|
12
|
-
- Stated intent of each new identifier: [INTENT]
|
|
13
|
-
|
|
14
|
-
ID prefix: `find`.
|
|
15
|
-
|
|
16
|
-
[ONE-PARAGRAPH FRAME: list every fresh identifier introduced by the diff. State the audit goal: for each identifier, verify the body's behavior matches the contract the name asserts — not broader, not narrower.]
|
|
17
|
-
|
|
18
|
-
## Source material ([N] files/sections, all lines in scope)
|
|
19
|
-
|
|
20
|
-
[INLINE each fresh identifier in context — the constant declaration with the value, the function signature with the body, the flag with every assignment / reset site, the regex source with surrounding usage.]
|
|
21
|
-
|
|
22
|
-
## Binding counterexample protocol (run before every Shape B "clean" verdict)
|
|
23
|
-
|
|
24
|
-
For every regex, brace or token scanner, or word-list the diff adds or edits, build at least three concrete inputs and trace each one through the pattern by hand:
|
|
25
|
-
|
|
26
|
-
- One intended match the pattern must catch.
|
|
27
|
-
- One near-miss the pattern must reject (hyphen vs underscore, a value reference vs a declaration, a substring of a longer word).
|
|
28
|
-
- One structural edge that stresses the scanner: a destructured parameter, a quoted key, a nested brace, an escaped delimiter.
|
|
29
|
-
|
|
30
|
-
Record what the pattern returns for each input. A wrong answer on any input is a Shape A finding, cited with the breaking input; a clean verdict is earned only after all three inputs return the contract's answer. `hooks/blocking/` is the priority surface — its patterns gate every write.
|
|
31
|
-
|
|
32
|
-
Two canonical failures to probe for:
|
|
33
|
-
- A brace scan that counts from the signature `(` and reads a destructured parameter (`renderRow({ id, label })`) as the start of the function body.
|
|
34
|
-
- A `\bschema\b` pattern that matches the value in `{ label: schema }`, not just the `schema` declaration it targets.
|
|
35
|
-
|
|
36
|
-
## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
|
|
37
|
-
|
|
38
|
-
**P1. Boolean / flag names assert state the body keeps**
|
|
39
|
-
- For every new `is_*` / `has_*` / `was_*` / `should_*` flag, trace the body. Every set site must be paired with a reset site that fires when the named condition becomes false.
|
|
40
|
-
- Adversarial probes: (a) grep every assignment to the flag — count set-true vs set-false; (b) for AST-driven flags, walk the visit method and confirm scope-exit semantics (def visited → flag set; dedent / function-exit → flag reset); (c) construct an input where the flag should be false after a region that set it true — does the code agree.
|
|
41
|
-
|
|
42
|
-
**P2. Predicate-name breadth matches body coverage**
|
|
43
|
-
- For every new `_is_*` / `_has_*` / `_can_*` predicate function, list the body's `return True` branches. The named predicate must hold on the union of those branches and nothing else.
|
|
44
|
-
- Adversarial probes: (a) construct an input that satisfies the name but returns False — that is a P2 narrowness finding; (b) construct an input that does not satisfy the name but returns True — that is a P2 breadth finding; (c) check neighbor predicates — is the body's contract the responsibility of a different helper.
|
|
45
|
-
|
|
46
|
-
**P3. Regex name vs regex shape** ⭐ canonical P case
|
|
47
|
-
- For every new `*_PATTERN` / `*_REGEX` constant, read the regex source. Confirm the anchors (`^`, `$`, `\b`, lookarounds) match the name's promised shape. `FILE_PATH_PATTERN` implies path shape; the regex must include path-shape anchors (slash count, segment shape, length bounds) enough to reject non-path inputs.
|
|
48
|
-
- Adversarial probes: (a) construct 3 non-matching inputs that the regex's name says it should reject — does the regex reject them; (b) construct 3 inputs that satisfy the regex but violate the name — those are P3 findings; (c) check sibling regexes in the same module — is the new regex anchored consistently with them.
|
|
49
|
-
|
|
50
|
-
**P4. Helper-function name vs return contract**
|
|
51
|
-
- For every new helper function, compare the name to the return shape AND the matching semantics. `_split_module_stem_prefix` should split on a stem-prefix boundary; if the returned prefix substring-matches unrelated stems, the matching semantics diverge from the name.
|
|
52
|
-
- Adversarial probes: (a) feed the helper an input class the name names — is the return what the name implies; (b) feed it an input the name excludes — does the helper still produce output; (c) check downstream callers — do they consume the return on the contract the name implies.
|
|
53
|
-
|
|
54
|
-
**P5. Word-list / replacement-table precision**
|
|
55
|
-
- For every new reference list, walk each entry. Every entry must satisfy the named class. A list named `HEAVY_WORDS_TO_REPLACE` may not contain words that are ordinary in legitimate user inputs.
|
|
56
|
-
- Adversarial probes: (a) for each entry, construct 3 legitimate-looking inputs containing it — would the gate fire on legitimate writing; (b) check entry density against the named class — is the list curated or kitchen-sink; (c) propose 3 entries that fit the named class better and 3 entries that do not fit — does the list match the proposed-good or the proposed-bad set.
|
|
57
|
-
|
|
58
|
-
**P6. Class / module name vs scope**
|
|
59
|
-
- For every new class or module, list the responsibilities (exported symbols, dispatched calls). Confirm each fits the named scope.
|
|
60
|
-
- Adversarial probes: (a) list exported symbols and ask whether each matches the named scope; (b) check imports — does the module pull in unrelated subsystems; (c) check call-graph centrality — does the class own one thing or many.
|
|
61
|
-
|
|
62
|
-
**P7. Reverse: name understates what the body does**
|
|
63
|
-
- For every new identifier, ask whether the body produces effects the name does not promise (side effects, more return values, broader input acceptance, hidden state mutation). A future caller relying on the narrow name will be surprised.
|
|
64
|
-
- Adversarial probes: (a) walk side effects (writes, network calls, global mutation) — are they named in the contract; (b) walk return tuples / objects — is every component named; (c) walk input acceptance — does the helper silently accept inputs outside its named class.
|
|
65
|
-
|
|
66
|
-
## Cross-bucket questions to answer at the end
|
|
67
|
-
|
|
68
|
-
Q1: Across all 7 sub-buckets, which identifier's name-vs-body gap is most likely to cause false positives in a production gate? Cite the identifier and the input class that trips it.
|
|
69
|
-
|
|
70
|
-
Q2: Which identifier is at highest risk of being relied on by a future caller based on the name alone, with the broader / narrower body causing a regression? Cite the identifier and the assumed contract.
|
|
71
|
-
|
|
72
|
-
Q3: Which identifier most clearly shows the body should be renamed (the name is honest about intent and the body is wrong) vs the name should be tightened (the body is the contract and the name overpromises)? Cite the identifier and the recommended direction.
|
|
73
|
-
|
|
74
|
-
## Output
|
|
75
|
-
|
|
76
|
-
Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket P1-P7, produce Shape A or Shape B (with ≥3 probes). Each Shape A finding must cite (a) the identifier file:line, (b) one concrete input that shows the name-vs-body gap, and (c) the recommended fix direction (rename vs body-tighten). Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 identifiers whose names overstate what the body does — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
# Worked example: jl-cmd/claude-dev-env PR #508
|
|
81
|
-
|
|
82
|
-
Audit jl-cmd/claude-dev-env PR #508 for **Category P only** (name / regex / word-list vs behavior-contract precision). Skip A-O.
|
|
83
|
-
|
|
84
|
-
PR #508 ships the plain-language blocker hook. Two fresh identifiers in `plain_language_blocker_constants.py` show the canonical P shapes:
|
|
85
|
-
|
|
86
|
-
- `FILE_PATH_PATTERN` (line 286) — a regex of shape `(\S+/\S+)` named for file paths but unanchored. Probes: `client/server`, `and/or`, `TCP/IP`, `lookup/replace` all match and are silently exempted from the plain-language scan even though none is a file path. **P3 finding.**
|
|
87
|
-
- `HARD_DENY_REPLACEMENT_TERMS` (line 247) — a hard-deny replacement-by-term list named for ban-worthy heavy words but containing `command`, `address`, `function`, `subject`, `same`, `such`, `said`, `it is`, `there is`, `however`, `forward`. Each entry trips on ordinary technical English (`run the command`, `the address bar`, `validate the function`). **P5 finding.**
|
|
88
|
-
|
|
89
|
-
Expected output: two P-class findings with cited file:line of the constant, cited example inputs (`client/server`, `run the command`), and recommended direction (anchor the regex to need repo-relative paths; curate the deny-list to drop dev-domain false positives).
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
Audit [REPO/ARTIFACT] [TARGET_ID] for **Category Q only** (cross-surface claim consistency — terminology, PR-description claims, message-vs-guard). Skip A–P. Sub-bucket forced-exhaustion mode: Category Q is decomposed into 3 sub-buckets below. Each sub-bucket needs at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
|
|
2
|
-
|
|
3
|
-
[ARTIFACT METADATA]
|
|
4
|
-
|
|
5
|
-
- Title / one-line summary: [TITLE]
|
|
6
|
-
- Base ref / SHA (state before the change): [BASE_SHA]
|
|
7
|
-
- Head ref / SHA at audit time (state after the change): [HEAD_SHA]
|
|
8
|
-
- Changed surfaces (file + line range + symbol/region name): [CHANGED_SURFACES]
|
|
9
|
-
- New or read identifiers, API fields, and config keys the diff touches: [DIFF_TERMS]
|
|
10
|
-
- Changed prose surfaces (README, doc tables, inline prose, log and error strings): [CHANGED_PROSE_SURFACES]
|
|
11
|
-
- PR description body text: [PR_BODY]
|
|
12
|
-
- Stated intent of the change: [INTENT]
|
|
13
|
-
|
|
14
|
-
ID prefix: `find`.
|
|
15
|
-
|
|
16
|
-
[ONE-PARAGRAPH FRAME: describe what the diff changed in plain English, naming the identifiers and config keys it introduces, the PR-body claims it makes, and the messages it adds or edits. State the audit goal: find any surface whose wording contradicts another surface that must agree with it — a prose term that misspells a code field, a PR-body claim the diff or repo does not bear out, or a message that asserts more than the guard gating it guarantees.]
|
|
17
|
-
|
|
18
|
-
## Source material ([N] files/sections, all lines in scope)
|
|
19
|
-
|
|
20
|
-
[INLINE THE FULL DIFF — the changed lines and enough surrounding context to show each identifier's definition and each message's guard.]
|
|
21
|
-
|
|
22
|
-
[ALSO INLINE the PR description body text and every changed prose surface — README sections, doc tables, runbooks, log and error strings — so the agent can pair each claim with the code it names.]
|
|
23
|
-
|
|
24
|
-
## Sub-buckets (each needs a Shape A finding OR Shape B with ≥3 adversarial probes)
|
|
25
|
-
|
|
26
|
-
**Q1. Term sweep**
|
|
27
|
-
- Pull out every identifier, API field, and config key the diff introduces or reads. For each, sweep all changed prose surfaces for a near-miss variant — a hyphen or a space in place of an underscore, a singular where the code is plural (or the reverse), a shared prefix with a divergent tail.
|
|
28
|
-
- Run `packages/claude-dev-env/_shared/pr-loop/scripts/terminology_sweep.py` for mechanical recall. It reads a unified diff on stdin or via `--diff-file`, prints one `file:line` finding per near-miss, exits 1 when findings exist and 0 when clean. Feed it the PR diff, then adjudicate each finding: a near-miss can be an intentional distinct term, so cite the code definition and the prose line and judge whether they name the same thing.
|
|
29
|
-
- Adversarial probes when the sweep is clean: (a) check camelCase versus snake_case spellings of the same field across code and docs; (b) check singular/plural drift on collection field names; (c) check a shared-prefix family (`retry_budget`, `retry_count`, `retry_limit`) for a doc that names the wrong member.
|
|
30
|
-
|
|
31
|
-
**Q2. PR-description claim verification**
|
|
32
|
-
- Read the PR body. List each falsifiable claim — a dead-code removal, a performance or ceiling number, a "last caller" assertion, a "no remaining X" cleanup claim.
|
|
33
|
-
- Verify each claim two ways against the diff and the repo. A dead-code claim gets a reference search across the repo for the named symbol. A ceiling or performance claim gets checked against the config value the diff sets and the call sites it touches. Cite the PR-body line and the diff or repo line that confirms or contradicts it.
|
|
34
|
-
- Adversarial probes when the body looks correct: (a) search the repo for a surviving caller of any symbol the body calls dead; (b) compare each stated number against the config constant the diff sets; (c) check whether a "this finishes the cleanup" claim leaves a matching pattern in an unchanged sibling file.
|
|
35
|
-
|
|
36
|
-
**Q3. Message-vs-guard consistency**
|
|
37
|
-
- For every log, error, and evidence string the diff adds or edits, read the branch condition that gates it. The message text matches what that guard guarantees: a string asserting an outcome fires only on a branch that reaches the outcome.
|
|
38
|
-
- Check temporal wording against the lifetime of the state variable the message names: "this run" versus "this round", "is open" versus "was opened", "reached" versus "attempted". Cite the message line and the guard or state-variable line.
|
|
39
|
-
- Adversarial probes when messages look aligned: (a) trace each success message back to the return value or flag it claims and confirm the branch cannot reach it with the opposite value; (b) check every "missing" / "not found" error against the exception class the branch catches; (c) check every counter-based message against where the counter resets.
|
|
40
|
-
|
|
41
|
-
## Cross-bucket questions to answer at the end
|
|
42
|
-
|
|
43
|
-
Q-a: Which single claim in this change is most likely to mislead a reader at runtime — a prose term that names a field the code does not define, a PR-body fact the diff contradicts, or a message that overstates its guard? Cite both surfaces by `path:line`.
|
|
44
|
-
|
|
45
|
-
Q-b: Which surface is the strongest witness to the contradiction — the doc, PR body, or message a reader would trust while the code says otherwise?
|
|
46
|
-
|
|
47
|
-
Q-c: Is there a term the diff introduces that appears correct in the code yet drifts across two or more prose surfaces at once? Cite each prose line and the code definition.
|
|
48
|
-
|
|
49
|
-
## Output
|
|
50
|
-
|
|
51
|
-
Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket Q1-Q3, produce Shape A or Shape B (with ≥3 probes). Each Shape A finding cites BOTH surfaces — the claim and the surface that contradicts it — by `path:line`. The `failure_mode` describes the contradiction between the two surfaces. Cross-bucket Q-a through Q-c answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 cross-surface contradictions — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""UserPromptSubmit hook. Adds the style reminder to every message.
|
|
3
|
-
|
|
4
|
-
::
|
|
5
|
-
|
|
6
|
-
user sends a message
|
|
7
|
-
|
|
|
8
|
-
v
|
|
9
|
-
UserPromptSubmit fires -> additionalContext: "small words. few words. always. forever."
|
|
10
|
-
|
|
|
11
|
-
v
|
|
12
|
-
the model sees the reminder this turn, and each turn after
|
|
13
|
-
|
|
14
|
-
Claude Code and Codex CLI both read the same ``additionalContext`` shape on
|
|
15
|
-
their ``UserPromptSubmit`` event. The Claude hook includes the skill reminder
|
|
16
|
-
when its flag is present. The unflagged Codex hook prints style alone.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
from __future__ import annotations
|
|
20
|
-
|
|
21
|
-
import json
|
|
22
|
-
import sys
|
|
23
|
-
|
|
24
|
-
import _path_setup # noqa: F401
|
|
25
|
-
|
|
26
|
-
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
|
|
27
|
-
from hooks_constants.style_reminder_prompt_constants import STYLE_REMINDER_PROMPT
|
|
28
|
-
from skill_loaded_reminder import reminder_for
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _additional_context_for_prompt() -> str:
|
|
32
|
-
if "--include-skill-reminder" not in sys.argv[1:]:
|
|
33
|
-
return STYLE_REMINDER_PROMPT
|
|
34
|
-
hook_input = read_hook_input_dictionary_from_stdin()
|
|
35
|
-
if hook_input is None:
|
|
36
|
-
return STYLE_REMINDER_PROMPT
|
|
37
|
-
skill_reminder = reminder_for(hook_input)
|
|
38
|
-
if skill_reminder is None:
|
|
39
|
-
return STYLE_REMINDER_PROMPT
|
|
40
|
-
return STYLE_REMINDER_PROMPT + "\n" + skill_reminder
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def main() -> None:
|
|
44
|
-
"""Print ordered UserPromptSubmit additionalContext."""
|
|
45
|
-
payload = {
|
|
46
|
-
"hookSpecificOutput": {
|
|
47
|
-
"hookEventName": "UserPromptSubmit",
|
|
48
|
-
"additionalContext": _additional_context_for_prompt(),
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
sys.stdout.write(json.dumps(payload) + "\n")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if __name__ == "__main__":
|
|
55
|
-
main()
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""Tests for style_reminder_prompt. Checks the hook adds the style reminder."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import sys
|
|
5
|
-
from io import StringIO
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from unittest.mock import patch
|
|
8
|
-
|
|
9
|
-
_SESSION_DIR = Path(__file__).resolve().parent
|
|
10
|
-
_HOOKS_ROOT = _SESSION_DIR.parent
|
|
11
|
-
for each_sys_path_entry in (str(_SESSION_DIR), str(_HOOKS_ROOT)):
|
|
12
|
-
if each_sys_path_entry not in sys.path:
|
|
13
|
-
sys.path.insert(0, each_sys_path_entry)
|
|
14
|
-
|
|
15
|
-
import style_reminder_prompt as reminder
|
|
16
|
-
from hooks_constants.skill_loaded_reminder_constants import NOT_LOADED_REMINDER
|
|
17
|
-
from hooks_constants.style_reminder_prompt_constants import STYLE_REMINDER_PROMPT
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _run_main(all_arguments: list[str], hook_input: str) -> str:
|
|
21
|
-
"""Return the text main() prints."""
|
|
22
|
-
captured_stdout = StringIO()
|
|
23
|
-
with (
|
|
24
|
-
patch("sys.argv", ["style_reminder_prompt.py", *all_arguments]),
|
|
25
|
-
patch("sys.stdin", StringIO(hook_input)),
|
|
26
|
-
patch("sys.stdout", captured_stdout),
|
|
27
|
-
):
|
|
28
|
-
reminder.main()
|
|
29
|
-
return captured_stdout.getvalue()
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class TestStyleReminderPrompt:
|
|
33
|
-
def test_main_emits_user_prompt_submit_hook_specific_output(self) -> None:
|
|
34
|
-
emitted = json.loads(_run_main([], ""))
|
|
35
|
-
hook_output = emitted["hookSpecificOutput"]
|
|
36
|
-
assert hook_output["hookEventName"] == "UserPromptSubmit"
|
|
37
|
-
assert hook_output["additionalContext"] == STYLE_REMINDER_PROMPT
|
|
38
|
-
|
|
39
|
-
def test_unflagged_invocation_keeps_style_only_with_hook_input(self) -> None:
|
|
40
|
-
emitted = json.loads(_run_main([], json.dumps({"hook_event_name": "UserPromptSubmit"})))
|
|
41
|
-
assert emitted["hookSpecificOutput"]["additionalContext"] == STYLE_REMINDER_PROMPT
|
|
42
|
-
|
|
43
|
-
def test_flagged_invocation_combines_style_and_missing_skill_reminder(self) -> None:
|
|
44
|
-
emitted = json.loads(
|
|
45
|
-
_run_main(
|
|
46
|
-
["--include-skill-reminder"],
|
|
47
|
-
json.dumps({"hook_event_name": "UserPromptSubmit"}),
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
assert emitted["hookSpecificOutput"]["additionalContext"] == (
|
|
51
|
-
STYLE_REMINDER_PROMPT + "\n" + NOT_LOADED_REMINDER
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def test_flagged_invocation_keeps_style_only_after_skill_load(self, tmp_path: Path) -> None:
|
|
55
|
-
transcript_path = tmp_path / "session.jsonl"
|
|
56
|
-
transcript_path.write_text(
|
|
57
|
-
json.dumps(
|
|
58
|
-
{
|
|
59
|
-
"type": "assistant",
|
|
60
|
-
"message": {
|
|
61
|
-
"content": [
|
|
62
|
-
{
|
|
63
|
-
"type": "tool_use",
|
|
64
|
-
"name": "Skill",
|
|
65
|
-
"input": {"skill": "pstack:poteto-mode"},
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
)
|
|
71
|
-
+ "\n",
|
|
72
|
-
encoding="utf-8",
|
|
73
|
-
)
|
|
74
|
-
emitted = json.loads(
|
|
75
|
-
_run_main(
|
|
76
|
-
["--include-skill-reminder"],
|
|
77
|
-
json.dumps(
|
|
78
|
-
{"hook_event_name": "UserPromptSubmit", "transcript_path": str(transcript_path)}
|
|
79
|
-
),
|
|
80
|
-
)
|
|
81
|
-
)
|
|
82
|
-
assert emitted["hookSpecificOutput"]["additionalContext"] == STYLE_REMINDER_PROMPT
|
|
83
|
-
|
|
84
|
-
def test_flagged_invocation_without_hook_input_keeps_style_only(self) -> None:
|
|
85
|
-
emitted = json.loads(_run_main(["--include-skill-reminder"], ""))
|
|
86
|
-
assert emitted["hookSpecificOutput"]["additionalContext"] == STYLE_REMINDER_PROMPT
|