ravensight-playtest 0.5.2 → 0.7.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.
- package/README.md +319 -14
- package/addons/ravensight_driver/driver.gd +406 -8
- package/addons/ravensight_simulate/simulate.gd +360 -0
- package/docs/godot-driver.md +70 -8
- package/package.json +4 -3
- package/src/api/README.md +4 -1
- package/src/api/client.js +120 -3
- package/src/api/errors.js +17 -1
- package/src/api/http.js +43 -17
- package/src/assets/formats.js +267 -0
- package/src/assets/index.js +260 -0
- package/src/assets/integration.js +429 -0
- package/src/assets/inventory.js +337 -0
- package/src/assets/measure.js +392 -0
- package/src/cli.js +233 -0
- package/src/commands/assets.js +62 -0
- package/src/commands/check.js +4 -1
- package/src/commands/create.js +734 -0
- package/src/commands/fakeCore.js +16 -1
- package/src/commands/feature.js +845 -0
- package/src/commands/fix.js +647 -70
- package/src/commands/init.js +19 -3
- package/src/commands/lists.js +206 -0
- package/src/commands/profile.js +25 -6
- package/src/commands/punch.js +116 -0
- package/src/commands/replay.js +204 -0
- package/src/commands/resume.js +7 -0
- package/src/commands/run.js +200 -24
- package/src/commands/tape.js +74 -0
- package/src/commands/template.js +70 -0
- package/src/commands/validate.js +414 -0
- package/src/commands/validateHosted.js +292 -0
- package/src/create/scaffold.js +192 -0
- package/src/run/acceptance.js +212 -0
- package/src/run/args.js +18 -1
- package/src/run/checks.js +705 -0
- package/src/run/dimension.js +95 -0
- package/src/run/drivers/godot-observation.js +59 -11
- package/src/run/drivers/godot-project.js +201 -31
- package/src/run/drivers/godot.js +532 -45
- package/src/run/drivers/web.js +18 -0
- package/src/run/evidence.js +170 -0
- package/src/run/featureLoop.js +288 -0
- package/src/run/fixLoop.js +82 -12
- package/src/run/git.js +110 -20
- package/src/run/json.js +65 -9
- package/src/run/labsOutcome.js +136 -0
- package/src/run/liveWork.js +103 -0
- package/src/run/model.js +105 -5
- package/src/run/openPr.js +107 -0
- package/src/run/paths.js +4 -1
- package/src/run/pendingRegister.js +81 -0
- package/src/run/personaLoop.js +111 -4
- package/src/run/profile.js +68 -7
- package/src/run/replay.js +236 -0
- package/src/run/repoTools.js +227 -27
- package/src/run/report.js +7 -1
- package/src/run/resume.js +19 -1
- package/src/run/secretScan.js +35 -0
- package/src/run/settle.js +41 -0
- package/src/run/tools.js +33 -4
- package/src/run/trace.js +124 -0
- package/src/simulation/godotPack.js +112 -0
- package/src/ui/index.js +61 -9
- package/src/upload/allowlist.js +6 -2
- package/src/upload/index.js +5 -4
- package/templates/godot-2d/README.md +36 -0
- package/templates/godot-2d/addons/ravensight/LICENSE +21 -0
- package/templates/godot-2d/addons/ravensight/Ravensight.gd +1823 -0
- package/templates/godot-2d/export_presets.cfg +27 -0
- package/templates/godot-2d/gitignore +10 -0
- package/templates/godot-2d/project.godot +58 -0
- package/templates/godot-2d/scenes/main.tscn +67 -0
- package/templates/godot-2d/scripts/autoload/game_config.gd +83 -0
- package/templates/godot-2d/scripts/autoload/game_state.gd +44 -0
- package/templates/godot-2d/scripts/autoload/telemetry.gd +37 -0
- package/templates/godot-2d/scripts/main.gd +32 -0
- package/templates/godot-2d/scripts/player.gd +10 -0
- package/templates/godot-2d/scripts/state_hook.gd +16 -0
- package/templates/godot-2d/template.json +14 -0
- package/templates/godot-2d/tools/inject_release_key.sh +19 -0
- package/templates/godot-2d/tools/selftest.gd +70 -0
- package/templates/godot-3d/README.md +49 -0
- package/templates/godot-3d/addons/ravensight/LICENSE +21 -0
- package/templates/godot-3d/addons/ravensight/Ravensight.gd +1823 -0
- package/templates/godot-3d/export_presets.cfg +27 -0
- package/templates/godot-3d/gitignore +10 -0
- package/templates/godot-3d/project.godot +56 -0
- package/templates/godot-3d/scenes/main.tscn +154 -0
- package/templates/godot-3d/scripts/autoload/game_config.gd +83 -0
- package/templates/godot-3d/scripts/autoload/game_state.gd +44 -0
- package/templates/godot-3d/scripts/autoload/telemetry.gd +37 -0
- package/templates/godot-3d/scripts/beacon.gd +20 -0
- package/templates/godot-3d/scripts/main.gd +49 -0
- package/templates/godot-3d/scripts/player.gd +19 -0
- package/templates/godot-3d/scripts/state_hook.gd +25 -0
- package/templates/godot-3d/template.json +16 -0
- package/templates/godot-3d/tools/inject_release_key.sh +19 -0
- package/templates/godot-3d/tools/selftest.gd +77 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix v2's acceptance contract and its Labs gate.
|
|
3
|
+
*
|
|
4
|
+
* The contract is written from the finding BEFORE anything is edited, sent at
|
|
5
|
+
* register, and frozen by the server on the job. From then on it is the only
|
|
6
|
+
* thing the verify brief is built from, and the apply step reads it but cannot
|
|
7
|
+
* change it: a repair turn is told what failed, never handed a new definition
|
|
8
|
+
* of success.
|
|
9
|
+
*
|
|
10
|
+
* Shape (the server's field names): `{ expected, actual, reproSteps[],
|
|
11
|
+
* regressionPersonas[] }`.
|
|
12
|
+
*
|
|
13
|
+
* Fix v2 is a Labs (Beta) capability, and the server decides whether it is
|
|
14
|
+
* on, not a client flag. Without a server that says so, and without a job
|
|
15
|
+
* that comes back carrying the frozen contract, `fix` behaves exactly as it
|
|
16
|
+
* did before Fix v2.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Field limits, so a contract built from a long finding still fits. */
|
|
20
|
+
export const ACCEPTANCE_LIMITS = Object.freeze({ text: 1000, steps: 20, step: 300, personas: 5 });
|
|
21
|
+
|
|
22
|
+
/** The Labs capability this module depends on. */
|
|
23
|
+
export const FIX_V2_CAPABILITY = 'fix_v2';
|
|
24
|
+
|
|
25
|
+
function text(value, limit) {
|
|
26
|
+
if (typeof value !== 'string') return '';
|
|
27
|
+
const trimmed = value.trim();
|
|
28
|
+
return trimmed.length <= limit ? trimmed : trimmed.slice(0, limit);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build the contract from a finding. Pure; nothing here asks a model.
|
|
33
|
+
*
|
|
34
|
+
* - `actual` is what the persona saw: the finding's own `actual`, else its
|
|
35
|
+
* description, else its title.
|
|
36
|
+
* - `expected` is the finding's own `expected`, else the plain negation of
|
|
37
|
+
* the title, which is what "fixed" means when nobody wrote more.
|
|
38
|
+
* - `reproSteps` are the finding's steps, as recorded.
|
|
39
|
+
* - `regressionPersonas` is the persona who filed it, who also plays verify.
|
|
40
|
+
*
|
|
41
|
+
* @param {Object} finding
|
|
42
|
+
* @param {{persona?: string|null, reproSteps?: Array<string>}} [options]
|
|
43
|
+
* @returns {{expected: string, actual: string, reproSteps: Array<string>, regressionPersonas: Array<string>}}
|
|
44
|
+
*/
|
|
45
|
+
export function buildAcceptance(finding = {}, { persona = null, reproSteps = [] } = {}) {
|
|
46
|
+
const title = text(finding.title, ACCEPTANCE_LIMITS.text);
|
|
47
|
+
const actual = text(finding.actual, ACCEPTANCE_LIMITS.text)
|
|
48
|
+
|| text(finding.description, ACCEPTANCE_LIMITS.text)
|
|
49
|
+
|| title
|
|
50
|
+
|| 'the behaviour the finding describes';
|
|
51
|
+
const expected = text(finding.expected, ACCEPTANCE_LIMITS.text)
|
|
52
|
+
|| (title ? `This no longer happens: ${title}` : 'The behaviour the finding describes no longer happens.');
|
|
53
|
+
const personas = [persona || finding.persona].filter(entry => typeof entry === 'string' && entry.trim());
|
|
54
|
+
return freeze({
|
|
55
|
+
expected,
|
|
56
|
+
actual,
|
|
57
|
+
reproSteps: reproSteps
|
|
58
|
+
.map(step => text(String(step), ACCEPTANCE_LIMITS.step))
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.slice(0, ACCEPTANCE_LIMITS.steps),
|
|
61
|
+
regressionPersonas: [...new Set(personas)].slice(0, ACCEPTANCE_LIMITS.personas)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The contract the server froze, read from a register answer, or null when
|
|
67
|
+
* the server kept none (an older server, or Labs off). Null means the standard fix.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} registered
|
|
70
|
+
* @returns {Object|null}
|
|
71
|
+
*/
|
|
72
|
+
export function frozenAcceptance(registered) {
|
|
73
|
+
const raw = (registered && (registered.acceptance || (registered.job && registered.job.acceptance))) || null;
|
|
74
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
75
|
+
const expected = text(raw.expected, ACCEPTANCE_LIMITS.text);
|
|
76
|
+
const actual = text(raw.actual, ACCEPTANCE_LIMITS.text);
|
|
77
|
+
if (!expected || !actual) return null;
|
|
78
|
+
const list = value => (Array.isArray(value) ? value.filter(entry => typeof entry === 'string' && entry.trim()) : []);
|
|
79
|
+
return freeze({
|
|
80
|
+
expected,
|
|
81
|
+
actual,
|
|
82
|
+
reproSteps: list(raw.reproSteps),
|
|
83
|
+
regressionPersonas: list(raw.regressionPersonas)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Whether the server says Fix v2 is live for this game. Anything other than a
|
|
89
|
+
* clear yes is no: a missing route (an older server), an error, Labs off for
|
|
90
|
+
* the game, or the capability's kill switch.
|
|
91
|
+
*
|
|
92
|
+
* Accepted answers: `{ enabled, capabilities: { fix_v2: true } }` or
|
|
93
|
+
* `{ capabilities: { fix_v2: { live: true } } }` (`enabled: true` is read the
|
|
94
|
+
* same as `live`).
|
|
95
|
+
*
|
|
96
|
+
* @param {Object} api
|
|
97
|
+
* @param {string} gameId
|
|
98
|
+
* @returns {Promise<boolean>}
|
|
99
|
+
*/
|
|
100
|
+
export async function fixV2Live(api, gameId, { onUnfunded } = {}) {
|
|
101
|
+
if (!api || !api.labs || typeof api.labs.get !== 'function') return false;
|
|
102
|
+
let answer;
|
|
103
|
+
try {
|
|
104
|
+
answer = await api.labs.get(gameId);
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (!capabilityLive(answer, FIX_V2_CAPABILITY)) return false;
|
|
109
|
+
// A register would refuse it, and the ordinary fix is right there at the same price.
|
|
110
|
+
const labs = answer.labs && typeof answer.labs === 'object' ? answer.labs : answer;
|
|
111
|
+
if (labs.funding_met && labs.funding_met[FIX_V2_CAPABILITY] === false) {
|
|
112
|
+
if (onUnfunded) onUnfunded();
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Whether this game's traces should be uploaded: any Labs capability that reads
|
|
120
|
+
* them back is on for it. Never throws; no answer is no upload.
|
|
121
|
+
*
|
|
122
|
+
* @param {Object} api
|
|
123
|
+
* @param {string} gameId
|
|
124
|
+
* @returns {Promise<boolean>}
|
|
125
|
+
*/
|
|
126
|
+
export async function tracesWanted(api, gameId) {
|
|
127
|
+
if (!api || !api.labs || typeof api.labs.get !== 'function') return false;
|
|
128
|
+
try {
|
|
129
|
+
const answer = await api.labs.get(gameId);
|
|
130
|
+
return capabilityLive(answer, 'validate') || capabilityLive(answer, FIX_V2_CAPABILITY);
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {Object} answer
|
|
138
|
+
* @param {string} name
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
export function capabilityLive(answer, name) {
|
|
142
|
+
if (!answer || typeof answer !== 'object') return false;
|
|
143
|
+
const labs = answer.labs && typeof answer.labs === 'object' ? answer.labs : answer;
|
|
144
|
+
if (labs.enabled === false) return false;
|
|
145
|
+
const entry = labs.capabilities && labs.capabilities[name];
|
|
146
|
+
if (entry === true) return true;
|
|
147
|
+
return Boolean(entry && typeof entry === 'object' && (entry.live === true || entry.enabled === true));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The contract as the developer reads it, before agreeing to pay.
|
|
152
|
+
*
|
|
153
|
+
* @param {Object} acceptance
|
|
154
|
+
* @returns {Array<string>}
|
|
155
|
+
*/
|
|
156
|
+
export function acceptanceLines(acceptance) {
|
|
157
|
+
return [
|
|
158
|
+
'Acceptance contract (frozen at register; the fix is judged against this and nothing else):',
|
|
159
|
+
` expected: ${acceptance.expected}`,
|
|
160
|
+
` actual: ${acceptance.actual}`,
|
|
161
|
+
acceptance.reproSteps.length > 0
|
|
162
|
+
? ` repro: ${acceptance.reproSteps.map((step, index) => `${index + 1}. ${step}`).join(' ')}`
|
|
163
|
+
: ' repro: none recorded',
|
|
164
|
+
` verify and regression personas: ${acceptance.regressionPersonas.join(', ') || 'none'}`
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The contract as a prompt block, marked read-only.
|
|
170
|
+
*
|
|
171
|
+
* @param {Object} acceptance
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
export function acceptanceBlock(acceptance) {
|
|
175
|
+
return [
|
|
176
|
+
'<acceptance read_only="true">',
|
|
177
|
+
JSON.stringify(acceptance, null, 2),
|
|
178
|
+
'</acceptance>',
|
|
179
|
+
'The acceptance contract above was frozen before any edit. You cannot change it, and the fix is judged against it alone.'
|
|
180
|
+
].join('\n');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The verify run's brief, built from the frozen contract only: not from the
|
|
185
|
+
* finding, not from the plan, not from anything the apply step said.
|
|
186
|
+
*
|
|
187
|
+
* @param {Object|null} base the game's brief
|
|
188
|
+
* @param {Object} acceptance
|
|
189
|
+
* @returns {Object}
|
|
190
|
+
*/
|
|
191
|
+
export function verifyBriefFromAcceptance(base, acceptance) {
|
|
192
|
+
const fields = (base && base.fields) || {};
|
|
193
|
+
return {
|
|
194
|
+
...(base || {}),
|
|
195
|
+
fields: {
|
|
196
|
+
verify_task: `Check whether this still happens: ${acceptance.actual}`,
|
|
197
|
+
verify_expected: acceptance.expected,
|
|
198
|
+
verify_repro_steps: [...acceptance.reproSteps],
|
|
199
|
+
verify_instruction: 'Follow these steps first and say plainly whether the actual behaviour still happens or the expected behaviour happens instead. Then play as you normally would.',
|
|
200
|
+
...fields
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function freeze(value) {
|
|
206
|
+
for (const key of Object.keys(value)) {
|
|
207
|
+
if (Array.isArray(value[key])) Object.freeze(value[key]);
|
|
208
|
+
}
|
|
209
|
+
return Object.freeze(value);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export default { buildAcceptance, frozenAcceptance, fixV2Live, capabilityLive, acceptanceLines, acceptanceBlock, verifyBriefFromAcceptance };
|
package/src/run/args.js
CHANGED
|
@@ -20,6 +20,9 @@ const VALUED = new Map([
|
|
|
20
20
|
['--build-url', 'buildUrl'],
|
|
21
21
|
['--build-dir', 'buildDir'],
|
|
22
22
|
['--godot-project', 'godotProject'],
|
|
23
|
+
// The Godot binary, as check and the driver docs take it. RAVENSIGHT_GODOT
|
|
24
|
+
// and GODOT still work when it is not given.
|
|
25
|
+
['--godot-path', 'godotPath'],
|
|
23
26
|
['--cli', 'cli'],
|
|
24
27
|
['--max-actions', 'maxActions'],
|
|
25
28
|
['--concurrency', 'concurrency'],
|
|
@@ -40,11 +43,25 @@ const BOOLEAN = new Map([
|
|
|
40
43
|
// A long persona run: twice the actions, model budget and wall time of a
|
|
41
44
|
// standard run, priced by the server as its own unit.
|
|
42
45
|
['--long', 'long'],
|
|
46
|
+
// godot_driver only: let the game run in real time between actions instead
|
|
47
|
+
// of pausing it while the persona thinks.
|
|
48
|
+
['--realtime', 'realtime'],
|
|
43
49
|
// Native drivers only: register without the free automation probe first.
|
|
44
50
|
['--skip-preflight', 'skipPreflight'],
|
|
45
51
|
// Resume only: replay an interrupted run from the start instead of writing a
|
|
46
52
|
// partial report from the transcript it already has.
|
|
47
|
-
['--restart-interrupted', 'restartInterrupted']
|
|
53
|
+
['--restart-interrupted', 'restartInterrupted'],
|
|
54
|
+
// Fix only, Fix v2 (Labs beta): after a verified fix, ask, then commit the
|
|
55
|
+
// fix's files, push the branch and open a pull request with gh.
|
|
56
|
+
['--open-pr', 'openPr'],
|
|
57
|
+
// Fix only: run the standard fix even when Fix v2 is on for the game.
|
|
58
|
+
['--no-labs', 'noLabs'],
|
|
59
|
+
// Fix v2: let the checks run the project's own tests, and run on edits to
|
|
60
|
+
// test files.
|
|
61
|
+
['--allow-tests', 'allowTests'],
|
|
62
|
+
// Fix v2: let the checks import a project whose edits touched editor-side
|
|
63
|
+
// code (project.godot, export presets, addons, .godot, @tool scripts).
|
|
64
|
+
['--allow-tool-scripts', 'allowToolScripts']
|
|
48
65
|
]);
|
|
49
66
|
|
|
50
67
|
/** Options whose value is a number. */
|