finalize_local_revision.py 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. """Refresh local review artifacts and check the instruction-only Doubao revision."""
  2. from pathlib import Path
  3. import difflib
  4. import hashlib
  5. import json
  6. import re
  7. import runpy
  8. from urllib.parse import unquote
  9. OUT = Path(__file__).resolve().parent
  10. ROOT = OUT.parents[1]
  11. SKILL = ROOT / 'skills/ksf-workshop-runtime'
  12. def sha(path):
  13. return hashlib.sha256(path.read_bytes()).hexdigest()
  14. def main():
  15. baseline = dict(line.split(' ', 1)[::-1] for line in
  16. (OUT / 'before/FILES.sha256').read_text(encoding='utf-8').splitlines())
  17. assert len(baseline) == 41
  18. actual = {p.relative_to(SKILL).as_posix() for p in SKILL.rglob('*') if p.is_file()}
  19. assert actual == set(baseline) | {'FILES.sha256'}, actual ^ set(baseline)
  20. changes = [name for name, digest in baseline.items() if sha(SKILL / name) != digest]
  21. assert len(changes) == 20 and all(name.endswith('.md') for name in changes)
  22. runtime = [name for name in baseline if name.startswith('scripts/') or name == 'runtime-lock.json']
  23. assert len(runtime) == 7 and all(sha(SKILL / name) == baseline[name] for name in runtime)
  24. assert sha(SKILL / 'references/workshop-commercial-feasibility.md') == baseline['references/workshop-commercial-feasibility.md']
  25. # Check the recorded transformation against the originals without applying it again.
  26. update = runpy.run_path(str(OUT / 'update_local_skill.py'))
  27. for name in changes:
  28. before = (OUT / 'before' / name).read_text(encoding='utf-8')
  29. assert sha(OUT / 'before' / name) == baseline[name]
  30. expected = before
  31. for old, new in update['GENERAL'] + update['SPECIFIC'].get(name, []):
  32. expected = expected.replace(old, new)
  33. if name == 'SKILL.md':
  34. pos = expected.index('\n## ') + 1
  35. expected = expected[:pos] + update['ENVIRONMENT'] + expected[pos:]
  36. after = (SKILL / name).read_text(encoding='utf-8')
  37. assert after == expected, 'Unrecorded edit: ' + name
  38. patch = OUT / 'diff' / (name + '.diff')
  39. patch.write_text(''.join(difflib.unified_diff(
  40. before.splitlines(True), after.splitlines(True),
  41. fromfile='r6/' + name, tofile='local-doubao/' + name)), encoding='utf-8', newline='\n')
  42. refs = list((SKILL / 'references').rglob('*'))
  43. assert sum(p.suffix == '.md' for p in refs) == 31
  44. assert sum(p.suffix == '.json' for p in refs) == 2
  45. links = 0
  46. for path in SKILL.rglob('*.md'):
  47. content = path.read_text(encoding='utf-8')
  48. for target in re.findall(r'\[[^\]]*\]\(([^)\s]+)\)', content):
  49. if target.startswith(('https:', 'http:', '#')):
  50. continue
  51. target_path = unquote(target.split('#', 1)[0])
  52. if target_path:
  53. assert (path.parent / target_path).is_file(), (path, target)
  54. links += 1
  55. if path.name != 'SKILL.md':
  56. assert not re.search(r'付费|收费|计费|停费|paidCalls|零费用', content), path
  57. intake = (SKILL / 'references/workshop-intake.md').read_text(encoding='utf-8')
  58. assert len(re.findall(r'^\| D[12]-(?:\d+|N) ', intake, re.M)) == 13
  59. sealed = ROOT / 'docs/workshop-skill-release-20261109-r6/ksf-workshop-skill-update-20261109-r6.zip'
  60. assert sha(sealed) == 'c329432b9773cfb0a8e5d4a3e5c162b3778eab28d576b79875da6854f33c4dc5'
  61. manifest = ''.join(sha(SKILL / name) + ' ' + name + '\n' for name in baseline)
  62. (SKILL / 'FILES.sha256').write_text(manifest, encoding='utf-8', newline='\n')
  63. report = {
  64. 'kind': 'local-instruction-draft-not-installed-remotely', 'baseline': 'r6',
  65. 'changes': [{'path': name, 'beforeSha256': baseline[name],
  66. 'afterSha256': sha(SKILL / name)} for name in changes],
  67. 'skillSha256': sha(SKILL / 'SKILL.md'),
  68. 'manifestSha256': sha(SKILL / 'FILES.sha256'),
  69. 'publicFilesVerified': 42, 'manifestEntriesVerified': 41,
  70. 'relativeLinksVerified': links, 'referenceFilesVerified': 33,
  71. 'intakeStagesPreserved': 13, 'runtimeFilesChanged': 0,
  72. 'businessCostRulesUnchanged': True, 'sealedR6PackageModified': False,
  73. 'doubaoExecutionVerified': False, 'remoteInstalled': False,
  74. }
  75. (OUT / 'change-receipt.json').write_text(
  76. json.dumps(report, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
  77. print(json.dumps({k: v for k, v in report.items() if k != 'changes'}, ensure_ascii=False, indent=2))
  78. if __name__ == '__main__':
  79. main()