Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
60 lines
4.8 KiB
Python
60 lines
4.8 KiB
Python
# Row 149 (#1643) mutants: one per new check. Each edits one file in the worktree, the test files
|
|
# that cover row 149 run per package, and the file is restored afterwards.
|
|
import subprocess, os
|
|
W = os.path.expanduser('~/dewey-scratch/r149')
|
|
OUT = os.path.expanduser('~/dewey-scratch/r149-mut')
|
|
os.makedirs(OUT, exist_ok=True)
|
|
TOOLS = 'packages/harness/src/tools.mjs'
|
|
SR, MG = 'roles/security-reviewer.json', 'roles/merge-gate.json'
|
|
PROACTIVE = 'Within your grants, act on what you see without waiting to be asked, and\nraise a decision or send a message for anything outside them.\n\n'
|
|
ROUTING = ("Where a raise goes (PRD 1.5 REQ-DEC-2): a cross-role raise, or a gated\nraise that cites the project's current spec commit, goes to the\n"
|
|
"business's arbiter for its domain. Pass `domain` to `raise_decision`,\n`technical` or `delivery`; leaving it out means `delivery`. An uncited\n"
|
|
"gated raise, and every hard wait, goes to the human through the inbox.\n")
|
|
SR_MSG = ',\n "messaging": {\n "sendTo": ["human", "chief-of-staff", "pm", "cto", "coder"],\n "receiveFrom": ["human", "chief-of-staff", "pm", "cto", "coder"]\n }'
|
|
MG_MSG = ',\n "messaging": {\n "sendTo": ["human", "chief-of-staff", "pm", "cto"],\n "receiveFrom": ["human", "chief-of-staff", "pm", "cto"]\n }'
|
|
M = {
|
|
'T1-domain-param': (TOOLS, ' domain: str("The arbiter domain for a routed raise: technical or delivery. Leaving it out means delivery", { enum: ["technical", "delivery"] }),\n', ''),
|
|
'T2-domain-enum': (TOOLS, '{ enum: ["technical", "delivery"] }', '{ enum: ["technical", "delivery", "human"] }'),
|
|
'T3-domain-desc': (TOOLS, 'Leaving it out means delivery"', 'Optional"'),
|
|
'T4-raise-desc': (TOOLS, "A cross-role raise, or a gated raise citing the project's current spec commit, goes to the business's arbiter for its domain; an uncited gated raise or a hard wait goes to the human. ", ''),
|
|
'S1-sr-bash': (SR, '"tools": ["read", "grep", "find", "ls"]', '"tools": ["read", "grep", "find", "ls", "bash"]'),
|
|
'S2-sr-send': (SR, '"withinRole": ["review.verdict"]', '"withinRole": ["review.verdict", "message.send"]'),
|
|
'S3-sr-messaging': (SR, SR_MSG, ''),
|
|
'G1-mg-within': (MG, '"withinRole": [],\n "crossRole": ["git.merge.protected"]', '"withinRole": ["git.merge.protected"],\n "crossRole": []'),
|
|
'G2-mg-extra-cross': (MG, '"crossRole": ["git.merge.protected"]', '"crossRole": ["git.merge.protected", "task.reassign"]'),
|
|
'G3-mg-edit': (MG, '"tools": ["read", "grep", "find", "ls"]', '"tools": ["read", "grep", "find", "ls", "edit"]'),
|
|
'G4-mg-no-cto': (MG, '"receiveFrom": ["human", "chief-of-staff", "pm", "cto"]', '"receiveFrom": ["human", "chief-of-staff", "pm"]'),
|
|
'G5-mg-messaging': (MG, MG_MSG, ''),
|
|
'G6-mg-send': (MG, '"withinRole": []', '"withinRole": ["message.send"]'),
|
|
'H1-header': ('roles/merge-gate.md', '# Role contract: merge-gate\n', '# Merge gate\n'),
|
|
'P1-proactive-gone': ('roles/reviewer.md', PROACTIVE, ''),
|
|
'P2-proactive-outside': ('roles/coder.md', PROACTIVE, '## Elsewhere\n\n' + PROACTIVE),
|
|
'R1-routing-gone': ('roles/chief-of-staff.md', ROUTING, ''),
|
|
'R2-hard-wait': ('roles/pm.md', '`deploy`', '`git.merge.protected`'),
|
|
'R3-old-text': ('roles/pm.md', 'raise a decision and wait for', 'raise a decision for the human and wait for'),
|
|
}
|
|
FILES = [
|
|
('packages/business', ['tests/workflow-roles.test.mjs', 'tests/role.test.mjs', 'tests/assistant.test.mjs', 'tests/record-grants.test.mjs']),
|
|
('packages/bus', ['tests/workflow-roles.test.mjs', 'tests/csuite-messaging.test.mjs']),
|
|
('packages/harness', ['tests/raise-domain.test.mjs', 'tests/tools.test.mjs', 'tests/spec-tools.test.mjs']),
|
|
]
|
|
env = dict(os.environ, TMPDIR=os.path.expanduser('~/dewey-scratch/tmp'))
|
|
origs = {f: open(os.path.join(W, f)).read() for f in {v[0] for v in M.values()}}
|
|
try:
|
|
for name, (f, a, b) in M.items():
|
|
assert origs[f].count(a) == 1, name
|
|
for name, (f, a, b) in M.items():
|
|
open(os.path.join(W, f), 'w').write(origs[f].replace(a, b))
|
|
killed, log = [], ''
|
|
for pkg, tests in FILES:
|
|
r = subprocess.run(['node', '--test', *tests], cwd=os.path.join(W, pkg), env=env, capture_output=True, text=True, timeout=300, start_new_session=True)
|
|
log += f'== {pkg} rc={r.returncode}\n' + r.stdout + r.stderr
|
|
fails = sorted({l[2:].split(' (')[0] for l in r.stdout.splitlines() if l.startswith('✖ ') and 'failing tests' not in l})
|
|
if r.returncode != 0: killed.append(f'{pkg.split("/")[1]}: ' + '; '.join(fails))
|
|
open(os.path.join(W, f), 'w').write(origs[f])
|
|
open(os.path.join(OUT, f'{name}.log'), 'w').write(log)
|
|
print(name, 'KILLED' if killed else 'SURVIVED', '|', ' | '.join(killed)[:700], flush=True)
|
|
finally:
|
|
for f, s in origs.items(): open(os.path.join(W, f), 'w').write(s)
|
|
print('restored', all(open(os.path.join(W, f)).read() == s for f, s in origs.items()))
|