@giamme/forge 0.0.0-stage → 0.9.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 (152) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/LICENSE +21 -0
  3. package/README.md +194 -2
  4. package/SKILL.md +123 -0
  5. package/VERSION +1 -0
  6. package/docs/README.md +26 -0
  7. package/docs/add-ons.md +33 -0
  8. package/docs/artifacts.md +69 -0
  9. package/docs/commands.md +315 -0
  10. package/docs/dataset.md +20 -0
  11. package/docs/decomposed-runs.md +268 -0
  12. package/docs/development.md +95 -0
  13. package/docs/forge-ui.md +92 -0
  14. package/docs/fractal.md +181 -0
  15. package/docs/install.md +160 -0
  16. package/docs/memory.md +156 -0
  17. package/docs/models.md +118 -0
  18. package/docs/reviews.md +231 -0
  19. package/docs/safety.md +55 -0
  20. package/docs/troubleshooting.md +54 -0
  21. package/forge +79 -0
  22. package/package.json +27 -4
  23. package/references/dataset.md +329 -0
  24. package/references/decompose.md +667 -0
  25. package/references/fractal-dashboard.md +21 -0
  26. package/references/fractal.md +307 -0
  27. package/references/harnesses.md +365 -0
  28. package/references/jev.md +865 -0
  29. package/references/memory.md +228 -0
  30. package/references/planning.md +42 -0
  31. package/references/ripwire.md +47 -0
  32. package/references/runtime.md +64 -0
  33. package/references/solo.md +113 -0
  34. package/references/ui.md +112 -0
  35. package/registry.tsv +81 -0
  36. package/scripts/forge-artifact.sh +78 -0
  37. package/scripts/forge-contract.py +430 -0
  38. package/scripts/forge-dataset.py +6 -0
  39. package/scripts/forge-dispatch.sh +671 -0
  40. package/scripts/forge-fail-first.py +195 -0
  41. package/scripts/forge-fractal-dispatch.sh +4 -0
  42. package/scripts/forge-fractal-options.sh +54 -0
  43. package/scripts/forge-fractal.py +6 -0
  44. package/scripts/forge-guard.py +266 -0
  45. package/scripts/forge-install-fractal.sh +4 -0
  46. package/scripts/forge-install-ripwire.sh +52 -0
  47. package/scripts/forge-install.sh +135 -0
  48. package/scripts/forge-jev-options.sh +259 -0
  49. package/scripts/forge-jev.py +6 -0
  50. package/scripts/forge-lib-plans.sh +785 -0
  51. package/scripts/forge-lib-review.sh +494 -0
  52. package/scripts/forge-lib-task.sh +302 -0
  53. package/scripts/forge-lib-verify.sh +30 -0
  54. package/scripts/forge-memory.sh +575 -0
  55. package/scripts/forge-metrics.sh +39 -0
  56. package/scripts/forge-parallel-options.sh +76 -0
  57. package/scripts/forge-parallel.sh +1717 -0
  58. package/scripts/forge-plan-check.py +118 -0
  59. package/scripts/forge-prompt.py +45 -0
  60. package/scripts/forge-ripwire.py +136 -0
  61. package/scripts/forge-route.py +344 -0
  62. package/scripts/forge-runs.py +121 -0
  63. package/scripts/forge-runtime.py +586 -0
  64. package/scripts/forge-schedule.py +312 -0
  65. package/scripts/forge-shadow.py +305 -0
  66. package/scripts/forge-solo.sh +347 -0
  67. package/scripts/forge-ui-options.sh +42 -0
  68. package/scripts/forge-ui.py +435 -0
  69. package/scripts/forge-update.sh +71 -0
  70. package/scripts/forge-verify-coverage.py +956 -0
  71. package/scripts/forge-version.py +240 -0
  72. package/scripts/forge_dataset/__init__.py +439 -0
  73. package/scripts/forge_dataset/capture.py +330 -0
  74. package/scripts/forge_dataset/checks.py +109 -0
  75. package/scripts/forge_dataset/cli.py +233 -0
  76. package/scripts/forge_dataset/export.py +1009 -0
  77. package/scripts/forge_dataset/harvest.py +716 -0
  78. package/scripts/forge_dataset/importer.py +412 -0
  79. package/scripts/forge_dataset/outcomes.py +275 -0
  80. package/scripts/forge_dataset/routing.py +278 -0
  81. package/scripts/forge_fractal/__init__.py +84 -0
  82. package/scripts/forge_fractal/bridge.py +46 -0
  83. package/scripts/forge_fractal/cli.py +202 -0
  84. package/scripts/forge_fractal/dashboard.css +138 -0
  85. package/scripts/forge_fractal/dashboard.html +41 -0
  86. package/scripts/forge_fractal/dashboard.js +687 -0
  87. package/scripts/forge_fractal/decomposition.py +262 -0
  88. package/scripts/forge_fractal/execution.py +659 -0
  89. package/scripts/forge_fractal/inspection.py +261 -0
  90. package/scripts/forge_fractal/install.py +158 -0
  91. package/scripts/forge_fractal/invocation.py +94 -0
  92. package/scripts/forge_fractal/pipeline.py +124 -0
  93. package/scripts/forge_fractal/selection.py +179 -0
  94. package/scripts/forge_jev/__init__.py +376 -0
  95. package/scripts/forge_jev/backtest.py +322 -0
  96. package/scripts/forge_jev/cli.py +980 -0
  97. package/scripts/forge_jev/client.py +418 -0
  98. package/scripts/forge_jev/corpus.py +156 -0
  99. package/scripts/forge_jev/coupling.py +244 -0
  100. package/scripts/forge_jev/features.py +24 -0
  101. package/scripts/forge_jev/gates.py +100 -0
  102. package/scripts/forge_jev/memory.py +163 -0
  103. package/scripts/forge_jev/outcome.py +49 -0
  104. package/scripts/forge_jev/predict.py +204 -0
  105. package/scripts/forge_jev/questions.py +661 -0
  106. package/scripts/forge_jev/review.py +73 -0
  107. package/scripts/forge_jev/routing.py +271 -0
  108. package/scripts/forge_jev/subset.py +123 -0
  109. package/scripts/forge_jev/verify.py +453 -0
  110. package/scripts/forge_registry.py +140 -0
  111. package/ui/README.md +347 -0
  112. package/ui/THIRD_PARTY_NOTICES.md +177 -0
  113. package/ui/docs/api.md +970 -0
  114. package/ui/forge_ui/__init__.py +20 -0
  115. package/ui/forge_ui/__main__.py +135 -0
  116. package/ui/forge_ui/diffs.py +454 -0
  117. package/ui/forge_ui/executions.py +436 -0
  118. package/ui/forge_ui/export.py +214 -0
  119. package/ui/forge_ui/findings.py +132 -0
  120. package/ui/forge_ui/fractal.py +582 -0
  121. package/ui/forge_ui/models.py +216 -0
  122. package/ui/forge_ui/reader.py +1424 -0
  123. package/ui/forge_ui/server.py +312 -0
  124. package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
  125. package/ui/forge_ui/static/app.js +1079 -0
  126. package/ui/forge_ui/static/cloud3d.js +647 -0
  127. package/ui/forge_ui/static/cloudmodel.js +375 -0
  128. package/ui/forge_ui/static/cloudui.js +397 -0
  129. package/ui/forge_ui/static/convo.js +295 -0
  130. package/ui/forge_ui/static/datatable.js +232 -0
  131. package/ui/forge_ui/static/diffview.js +235 -0
  132. package/ui/forge_ui/static/favicon.svg +1 -0
  133. package/ui/forge_ui/static/files.js +776 -0
  134. package/ui/forge_ui/static/findings3d.js +134 -0
  135. package/ui/forge_ui/static/gantt.js +97 -0
  136. package/ui/forge_ui/static/graph.js +266 -0
  137. package/ui/forge_ui/static/icons.js +94 -0
  138. package/ui/forge_ui/static/index.html +67 -0
  139. package/ui/forge_ui/static/iso.js +816 -0
  140. package/ui/forge_ui/static/leaderboard.js +311 -0
  141. package/ui/forge_ui/static/map3d.js +45 -0
  142. package/ui/forge_ui/static/modelstats.js +249 -0
  143. package/ui/forge_ui/static/notify.js +179 -0
  144. package/ui/forge_ui/static/panel.js +562 -0
  145. package/ui/forge_ui/static/runmodel.js +667 -0
  146. package/ui/forge_ui/static/scene3d.js +1471 -0
  147. package/ui/forge_ui/static/search.js +120 -0
  148. package/ui/forge_ui/static/shell.js +137 -0
  149. package/ui/forge_ui/static/stagectl.js +150 -0
  150. package/ui/forge_ui/static/style.css +1073 -0
  151. package/ui/forge_ui/static/theme.js +25 -0
  152. package/ui/forge_ui/transcripts.py +608 -0
@@ -0,0 +1,132 @@
1
+ """QA findings, parsed by forge's own contract module (scripts/forge-contract.py), never re-implemented."""
2
+ from __future__ import annotations
3
+
4
+ import importlib.machinery
5
+ import importlib.util
6
+ from pathlib import Path
7
+ import re
8
+ import threading
9
+
10
+
11
+ # build_id() hashes this package only, not forge-contract.py: a server keeps the parser it loaded
12
+ # first, and an edit to the contract is picked up by the next server, not by a running one.
13
+ CONTRACT = Path(__file__).resolve().parents[2] / 'scripts' / 'forge-contract.py'
14
+ _LOCK = threading.Lock()
15
+ _STATE = {}
16
+ # The contract reads whole replies; this only bounds a pathological file.
17
+ _MAX_CHARS = 1000000
18
+ _SUMMARY_MAX = 240
19
+ _TEXT_MAX = 20000
20
+ _GATE = ('threshold', 'decision', 'worst', 'n_confirmed', 'n_unlabelled', 'n_below')
21
+
22
+
23
+ class _ReadOnlyLoader(importlib.machinery.SourceFileLoader):
24
+ def set_data(self, *args, **kwargs):
25
+ pass # forge-ui writes nothing, not even a .pyc beside forge's scripts
26
+
27
+
28
+ def _load():
29
+ with _LOCK:
30
+ if 'module' not in _STATE:
31
+ try:
32
+ spec = importlib.util.spec_from_file_location(
33
+ 'forge_contract_for_ui', CONTRACT, loader=_ReadOnlyLoader('forge_contract_for_ui', str(CONTRACT)))
34
+ module = importlib.util.module_from_spec(spec)
35
+ spec.loader.exec_module(module)
36
+ if not (callable(module.parse_findings) and hasattr(module._LOCATION, 'search')):
37
+ raise TypeError('not the forge contract module')
38
+ _STATE['module'] = module
39
+ except Exception: # a missing or broken contract only turns findings off
40
+ _STATE['module'] = None
41
+ return _STATE['module']
42
+
43
+
44
+ def available() -> bool:
45
+ return _load() is not None
46
+
47
+
48
+ # forge_verdict in scripts/forge-artifact.sh, as forge_dataset.harvest.verdict_of also ports it: only the
49
+ # reply's last non-blank line counts, and only as exactly `FORGE_VERDICT: PASS|FAIL`, optionally followed by
50
+ # a dash and a gloss. A verdict anywhere else (quoted, followed by more text) is no verdict, as for forge.
51
+ _VERDICT = re.compile(r'^FORGE_VERDICT: (PASS|FAIL)([ \t]+(—|–|-)( .*)?)?[ \t]*$')
52
+
53
+
54
+ def verdict(text):
55
+ """PASS, FAIL or None, exactly as forge decides it: from the reply's last non-blank line only.
56
+
57
+ None means forge recorded no verdict (UNKNOWN), which forge's gate treats as blocking."""
58
+ lines = [line for line in (text or '').splitlines() if line.strip()]
59
+ match = _VERDICT.match(lines[-1].rstrip('\r')) if lines else None
60
+ return match.group(1) if match else None
61
+
62
+
63
+ def _parse(text):
64
+ module = _load()
65
+ if module is None or not text:
66
+ return None, None
67
+ try:
68
+ found = []
69
+ for item in module.parse_findings(text[:_MAX_CHARS]):
70
+ severity = item.severity if type(item.severity) is int and 0 <= item.severity <= 3 else None
71
+ if item.status in ('CONFIRMED', 'PLAUSIBLE', 'UNSTATED') and isinstance(item.text, str):
72
+ found.append((item.status, severity, item.line if type(item.line) is int else None, item.text))
73
+ return module, found
74
+ except Exception:
75
+ return None, None
76
+
77
+
78
+ def summarize(text):
79
+ """FindingsSummary of a reply, or None when there is no text or no contract module."""
80
+ found = _parse(text)[1]
81
+ if found is None:
82
+ return None
83
+ result = dict(P0=0, P1=0, P2=0, P3=0, unlabelled=0, plausible=0, confirmed=0, worst=None)
84
+ for status, severity, _, _ in found:
85
+ if status == 'CONFIRMED':
86
+ result['confirmed'] += 1
87
+ if severity is None:
88
+ result['unlabelled'] += 1
89
+ else:
90
+ result['P%d' % severity] += 1
91
+ elif status == 'PLAUSIBLE':
92
+ result['plausible'] += 1
93
+ else:
94
+ result['unlabelled'] += 1 # UNSTATED counts as unlabelled, as in the gate
95
+ labelled = [severity for status, severity, _, _ in found if status == 'CONFIRMED' and severity is not None]
96
+ result['worst'] = 'P%d' % min(labelled) if labelled else None
97
+ return result
98
+
99
+
100
+ def listing(text, limit=200):
101
+ """The first `limit` Findings of a reply, or None when there is no text or no contract module."""
102
+ module, found = _parse(text)
103
+ if found is None:
104
+ return None
105
+ result = []
106
+ for status, severity, line, block in found[:limit]:
107
+ first = block.split('\n', 1)[0]
108
+ try:
109
+ match = module._LOCATION.search(first)
110
+ except Exception:
111
+ match = None
112
+ result.append(dict(severity=None if severity is None else 'P%d' % severity, status=status,
113
+ location=match.group(0) if match else None, line=line,
114
+ summary=first.strip()[:_SUMMARY_MAX],
115
+ text=block if len(block) <= _TEXT_MAX else block[:_TEXT_MAX] + '…'))
116
+ return result
117
+
118
+
119
+ def parse_gate(text):
120
+ """A qa.gate line: threshold, decision, worst, n_confirmed, n_unlabelled, n_below; None if malformed."""
121
+ line = next((item for item in (text or '').splitlines() if item.strip()), None)
122
+ parts = line.rstrip('\r').split('\t') if line else []
123
+ if len(parts) < len(_GATE):
124
+ return None
125
+ result = {}
126
+ for key, value in zip(_GATE, parts):
127
+ value = value.strip()
128
+ if key.startswith('n_'):
129
+ result[key] = int(value) if re.fullmatch(r'[0-9]{1,9}', value) else None
130
+ else:
131
+ result[key] = None if value in ('', '-') else value
132
+ return result