@entrorove/skill 0.1.84 → 0.1.85
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/CHANGELOG.md +5 -0
- package/DESIGN-GATES.md +18 -3
- package/SKILL.md +2 -2
- package/design-contracts.mjs +2 -1
- package/design-operations.mjs +23 -7
- package/design-store.mjs +36 -15
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/release-state.json +8 -8
- package/releases.json +6 -0
- package/remote-client.mjs +20 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.85 — 2026-10-23
|
|
4
|
+
|
|
5
|
+
- 修复设计证据失败恢复导致重复生成图片;复用原图与真实生成记录,补齐 CSS 自定义属性支持。执行计划补齐原始合约绑定和 Blueprint 参数约束,发送前指出缺失字段并禁止无效原样重试。
|
|
6
|
+
- Reuse original design images and truthful receipts after failed submissions, and support CSS custom properties. Expose workflow plan binding and raw Blueprint constraints, report missing payload fields before transmission, and prevent unchanged invalid retries.
|
|
7
|
+
|
|
3
8
|
## 0.1.84 — 2026-10-23
|
|
4
9
|
|
|
5
10
|
- 修复能力发现失败误报业务请求未知的问题:分别保留原业务与发现请求编号、传输原因和未发送记录,阻止轮询无持久结果的发现编号,并保留同一业务请求此前的未知执行记录。
|
package/DESIGN-GATES.md
CHANGED
|
@@ -41,10 +41,21 @@ source, account, database, runtime, deployment or write access.
|
|
|
41
41
|
|
|
42
42
|
## 3. Create mockups or prototypes under the original task authorization
|
|
43
43
|
|
|
44
|
+
Recover `design-status` and the original workflow design approval first. Reuse
|
|
45
|
+
the applicable original images, generation receipts and approved rules across
|
|
46
|
+
messages, compaction and stage handoffs. A failed proposal, schema correction,
|
|
47
|
+
implementation repair or reference refresh does not require another image call.
|
|
48
|
+
Generate only a missing baseline or an actual requested visual change that needs
|
|
49
|
+
new visual evidence. Do not repeatedly refine a structural prototype merely for
|
|
50
|
+
cosmetic fidelity; executable rules bind details the prototype omits. Honor any
|
|
51
|
+
explicit user prohibition on generation or testing.
|
|
52
|
+
|
|
44
53
|
Respect the user's choice: an effect mockup (`kind: mockup`) or a prototype /
|
|
45
54
|
wireframe (`kind: prototype`) is a valid visual baseline. Use the host's actual
|
|
46
|
-
image-generation or design/prototyping tool after research
|
|
47
|
-
|
|
55
|
+
image-generation or design/prototyping tool after research for a new baseline.
|
|
56
|
+
Reuse existing applicable PNGs with their truthful original receipts, including
|
|
57
|
+
when their generation predates refreshed research. Present several directions
|
|
58
|
+
only when the user requests alternatives, then export real PNG views of chosen pages and
|
|
48
59
|
required states at the target viewport sizes. A structural prototype can be
|
|
49
60
|
simple; bind theme colors, typography, spacing and components separately in its
|
|
50
61
|
rules so implementation cannot invent them. The same prototype may represent
|
|
@@ -63,7 +74,11 @@ Choose an isolated `http://127.0.0.1:<port>/` preview before approval. Invoke
|
|
|
63
74
|
{"schemaVersion":"entrorove.image-generation-record/1.0","tool":"actual-host-tool","requestId":"actual-tool-call-id","promptSha256":"<64 hex>","imageSha256":"<64 hex>","generatedAt":"<actual ISO timestamp>"}
|
|
64
75
|
```
|
|
65
76
|
|
|
66
|
-
These references bind bytes and
|
|
77
|
+
These references bind bytes and the actual generation time; never restamp a
|
|
78
|
+
reused image to make it newer than research. Identical evidence can be resubmitted
|
|
79
|
+
in the same revision. Different bytes preserve the old revision and require a
|
|
80
|
+
new revision only for real design changes; recovery itself never requires new
|
|
81
|
+
image generation. These records are not an independent signature
|
|
67
82
|
from the external image/design tool. Show the actual host tool result and
|
|
68
83
|
mockup/prototype views. Never present an owned test bitmap as a real user design.
|
|
69
84
|
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: Route a task selected for Entrorove among its eight skills, approve
|
|
|
5
5
|
|
|
6
6
|
# entrorove · 熵巡
|
|
7
7
|
|
|
8
|
-
Version: 0.1.
|
|
8
|
+
Version: 0.1.85.
|
|
9
9
|
|
|
10
10
|
Entrorove applies only to the task for which it is selected; reading these instructions, discovering capabilities or installing the package never selects or governs another task. Hooks are optional. Reading this document or calling an entry once does not execute the task. On every later turn, including after compaction, continue the original task and its required next operation before dependent work. Keep the original goal, scope, account, flow, cloud task and failure records. Never describe planning, a reported step or model output as verified delivery.
|
|
11
11
|
|
|
@@ -37,7 +37,7 @@ Capability discovery failures are separate from business execution. `ENTROROVE_D
|
|
|
37
37
|
For a member scope, follow a returned `localAction` before submitting execution evidence. `mutation-connect` obtains only the original task's limited editing pass. Its non-structural modify role requires known target source imports, preserves source imports and public contracts, and keeps unresolved origins, other observed dependencies and project configuration read only. Build scripts, macros and generated sources can remain opaque; source observations and this editing role never prove a complete compiled dependency graph or successful tests. After actual writes, refresh the original scope and run `mutation-settle`. Use the returned `previousPassId` for further edits. In a signed member-local plan, `workflow-observe-step` runs the original frozen command and tests without a validator private key. A server-signed `localAcceptance` advances local dependencies; a failed test does not. A frozen independent plan retains its independent validation requirement. Preserve the raw observation and its absent independent receipt. See [IDE-EXECUTION.md](IDE-EXECUTION.md#local-ide-acceptance).
|
|
38
38
|
7. Read [REUSE-DISCIPLINE.md](REUSE-DISCIPLINE.md) when modifying an existing system. The comparison baseline is the existing implementation in that same repository. It never means another project, another account, or copying external systems.
|
|
39
39
|
Never overwrite a newer package with older bytes, relabel an old archive as new, or roll back because the current state is uncertain. Package identity/version checks and immutable historical byte observations run in the guarded entries. A refusal requires preserved inputs and diagnosis; a `force`, `rollback`, timestamp or filename is not evidence. Intentional rollback uses the original independently signed merge rollback authorization, transaction journal, exact current/restore hashes and fresh original mutation pass. Preserve old failures, receipts and unknown effects; a successful rollback never makes the failed operation successful.
|
|
40
|
-
**Before preparing or writing any UI implementation, read [DESIGN-GATES.md](DESIGN-GATES.md).**
|
|
40
|
+
**Before preparing or writing any UI implementation, read [DESIGN-GATES.md](DESIGN-GATES.md).** First recover the original persisted design and approval. Reuse applicable original images, receipts and approved rules across messages, compaction and stage handoffs. Generate images only when a required visual baseline is actually missing or the user requested a visual change requiring one; input errors, failed gates, refreshed research and implementation corrections never justify drawing again. For a missing baseline, record the whole-site flow, fetch current layout references and framework rules, and create actual mockups or prototypes with the host tool. Respect the user's format choice; a prototype is valid. Obtain the original persisted design confirmation and carry it through the original scope, plan and mutation gates. Neither a caller flag nor text alone substitutes for actual evidence. Finish the authorized functional/browser acceptance before delivery; an explicit user prohibition on testing or image generation takes precedence and must never trigger substitute runs. Keep Entrorove unchanged in every language.
|
|
41
41
|
8. Read [DELIVERY-BOUNDARIES.md](DELIVERY-BOUNDARIES.md) before claiming a delivered stage or showing consumption. A delivered stage requires a clean boundary and uses `delivered-stage-N`. Blocked or unknown work can wait with recovery data intact; it is not a delivered stage. Display consumption only with a measured baseline.
|
|
42
42
|
9. For independent verified completion, submit actual independent execution evidence and run server verification and verdict. For a signed member-local plan, finish all original local steps and requirement checkpoints, then call the returned `workflow-verdict`; only its signed final `localAcceptance` means local acceptance is complete. Describe it as local execution and tests accepted, with independent validation not performed. Never invoke formal V, merge, Swarm or handoff to relabel it. Display the confirmed evaluation acknowledgement and preserve `receiptId`, `requestId`, execution generation and evaluation digest. “已回传” requires `acknowledgement.stored: true`. Missing or failed feedback remains visible. Active, waiting and unknown tasks retain recovery data.
|
|
43
43
|
|
package/design-contracts.mjs
CHANGED
|
@@ -26,7 +26,8 @@ const mockup = object({ kind: { enum: ['mockup', 'prototype'] }, pageId: identif
|
|
|
26
26
|
['kind', 'pageId', 'stateId', 'theme', 'viewport', 'image', 'generation']);
|
|
27
27
|
const assertion = object({ kind: { enum: ['visible', 'hidden', 'text', 'value', 'count', 'style', 'bounds'] },
|
|
28
28
|
field: identifier,
|
|
29
|
-
selector: words, value: { type: 'string', maxLength: 2000 }, count: integer(0, 1000),
|
|
29
|
+
selector: words, value: { type: 'string', maxLength: 2000 }, count: integer(0, 1000),
|
|
30
|
+
property: { type: 'string', maxLength: 128, pattern: '^(?:[A-Za-z][A-Za-z0-9-]*|--[A-Za-z_][A-Za-z0-9_-]*)$' },
|
|
30
31
|
bounds: object({ x: integer(0, 3840), y: integer(0, 10000), width: integer(1, 3840), height: integer(1, 10000),
|
|
31
32
|
tolerance: integer(0, 8) }, ['x', 'y', 'width', 'height', 'tolerance']) }, ['kind', 'selector']);
|
|
32
33
|
const network = object({ method: { enum: ['POST', 'PUT', 'PATCH', 'DELETE'] }, path: { ...text, pattern: '^/(?!/)' },
|
package/design-operations.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
3
|
import { sha256, canonical } from './local-integrity.mjs';
|
|
4
|
-
import { writeMutationFile14 } from './local-mutation-files.mjs';
|
|
5
4
|
import { designSchema } from './design-contracts.mjs';
|
|
6
5
|
import { validateDesignSite, validateDesignPolicy, designDigest, designBaselineDigest, designFail } from './design-policy.mjs';
|
|
7
|
-
import { designDirectory, designLoad, designSave, designMermaid, designCopy, assertDesignArtifacts, captureDesignSourceBaseline } from './design-store.mjs';
|
|
6
|
+
import { designDirectory, designLoad, designExisting, designSave, designWrite, designMermaid, designCopy, assertDesignGeneration, assertDesignArtifacts, captureDesignSourceBaseline } from './design-store.mjs';
|
|
8
7
|
import { fetchDesignReference, frameworkBinding, FRAMEWORK_DOC_HOSTS, assertFrozenDesignFrameworks } from './design-research.mjs';
|
|
9
8
|
import { assertDesignDeclaration } from './design-gate.mjs';
|
|
10
9
|
import { verifyDesignBrowser } from './design-browser.mjs';
|
|
@@ -13,7 +12,10 @@ import { assertDesignStageSources, designAcceptanceScope } from './design-stage.
|
|
|
13
12
|
|
|
14
13
|
export async function recordDesignFlow(input) {
|
|
15
14
|
const site = validateDesignSite(input.site), directory = await designDirectory(input), diagram = designMermaid(site);
|
|
16
|
-
const
|
|
15
|
+
const existing = await designExisting(directory, 'flow.json');
|
|
16
|
+
if (existing && canonical(existing.site) !== canonical(site)) designFail('ARTIFACT_CONFLICT', 'The flow changed; preserve the original and use a new design revision. Existing images can be reused with their original receipts');
|
|
17
|
+
const file = path.join(directory, 'site-flow.mmd'); await designWrite(file, Buffer.from(diagram));
|
|
18
|
+
if (existing) return { status: 'reused', ...existing, nextStep: 'design-research', grantsWriteAuthority: false };
|
|
17
19
|
const flow = { site, flow: { path: file, sha256: sha256(diagram), bytes: Buffer.byteLength(diagram) },
|
|
18
20
|
flowDigest: designDigest(site), flowRecordedAt: new Date().toISOString() };
|
|
19
21
|
await designSave(directory, 'flow.json', flow);
|
|
@@ -23,6 +25,16 @@ export async function recordDesignResearch(input) {
|
|
|
23
25
|
const directory = await designDirectory(input), flow = await designLoad(directory, 'flow.json'), sources = [], frameworks = [];
|
|
24
26
|
if (!Array.isArray(input.layoutReferences) || input.layoutReferences.length < 2 || input.layoutReferences.length > 8
|
|
25
27
|
|| !Array.isArray(input.frameworks) || input.frameworks.length > 16) designFail('RESEARCH', 'Provide two to eight actual layout references and the declared frameworks');
|
|
28
|
+
const existing = await designExisting(directory, 'research.json');
|
|
29
|
+
if (existing) {
|
|
30
|
+
const references = existing.sources.filter(source => source.purpose === 'layout').map(({ url, rationale }) => ({ url, rationale }));
|
|
31
|
+
const supplied = await Promise.all(input.frameworks.map(framework => frameworkBinding(input, framework)));
|
|
32
|
+
for (const framework of supplied) framework.officialUrls = framework.officialUrls.map(url =>
|
|
33
|
+
existing.sources.find(source => source.purpose === 'framework' && source.url === url)?.finalUrl ?? url);
|
|
34
|
+
if (canonical(references) !== canonical(input.layoutReferences) || canonical(supplied) !== canonical(existing.frameworks)) designFail('ARTIFACT_CONFLICT', 'Research inputs changed; preserve the original revision and reuse applicable existing images in a new revision');
|
|
35
|
+
if (Date.now() - Date.parse(existing.researchRecordedAt) > 30 * 86400_000) designFail('RESEARCH_STALE', 'Refresh references in a new revision; retain applicable original images and generation times');
|
|
36
|
+
return { status: 'reused', sources: existing.sources, frameworks: existing.frameworks, nextStep: 'reuse-existing-images-or-generate-if-missing-then-design-propose', grantsWriteAuthority: false };
|
|
37
|
+
}
|
|
26
38
|
for (const reference of input.layoutReferences) {
|
|
27
39
|
if (!reference.rationale?.trim()) designFail('RESEARCH', 'Explain why each mainstream layout fits these pages; popularity cannot be assumed');
|
|
28
40
|
sources.push({ url: reference.url, ...await fetchDesignReference(reference.url), purpose: 'layout', rationale: reference.rationale });
|
|
@@ -38,7 +50,7 @@ export async function recordDesignResearch(input) {
|
|
|
38
50
|
}
|
|
39
51
|
const result = { ...flow, sources, frameworks, researchRecordedAt: new Date().toISOString() };
|
|
40
52
|
await designSave(directory, 'research.json', result);
|
|
41
|
-
return { status: 'recorded', sources, frameworks, nextStep: '
|
|
53
|
+
return { status: 'recorded', sources, frameworks, nextStep: 'reuse-existing-images-or-generate-if-missing-then-design-propose', grantsWriteAuthority: false };
|
|
42
54
|
}
|
|
43
55
|
export async function proposeDesign(input) {
|
|
44
56
|
const directory = await designDirectory(input), research = await designLoad(directory, 'research.json');
|
|
@@ -46,15 +58,18 @@ export async function proposeDesign(input) {
|
|
|
46
58
|
const rules = designSchema('rules', input.rules), mockups = [];
|
|
47
59
|
const browser = await readDesignBrowserIdentity({ browserExecutablePath: rules.browser.executablePath });
|
|
48
60
|
if (canonical(browser) !== canonical(rules.browser)) designFail('BROWSER_CHANGED', 'Use the actual configured browser identity before confirming this design');
|
|
49
|
-
if (!Array.isArray(input.mockups) || input.mockups.length < 2 || input.mockups.length > 512) designFail('MOCKUPS', '
|
|
61
|
+
if (!Array.isArray(input.mockups) || input.mockups.length < 2 || input.mockups.length > 512) designFail('MOCKUPS', 'Provide actual page/state mockups or prototypes; reuse applicable original images and receipts');
|
|
62
|
+
const recordedAt = new Date().toISOString();
|
|
63
|
+
for (const item of input.mockups) await assertDesignGeneration(item, recordedAt);
|
|
50
64
|
for (const [index, item] of input.mockups.entries()) {
|
|
51
65
|
const image = await designCopy(directory, `mockup-${index}.png`, item.image.path);
|
|
52
66
|
const receipt = await designCopy(directory, `generation-${index}.json`, item.generation.receipt.path);
|
|
53
67
|
mockups.push({ ...item, image, generation: { ...item.generation, receipt } });
|
|
54
68
|
}
|
|
69
|
+
const existing = await designExisting(directory, 'proposal.json');
|
|
55
70
|
const body = { schemaVersion: 'entrorove.design-policy/1.0', designId: input.designId, flowId: input.flowId,
|
|
56
71
|
revision: input.revision, ...research, mockups, rules, sourceBaseline: await captureDesignSourceBaseline(input, directory, rules.uiPaths),
|
|
57
|
-
mockupsRecordedAt:
|
|
72
|
+
mockupsRecordedAt: existing?.mockupsRecordedAt ?? recordedAt };
|
|
58
73
|
const design = validateDesignPolicy({ ...body, baselineDigest: designBaselineDigest(body) });
|
|
59
74
|
await assertDesignStageSources(input, design);
|
|
60
75
|
await assertDesignArtifacts(design, input);
|
|
@@ -78,7 +93,8 @@ export async function readDesignStatus(input) {
|
|
|
78
93
|
: entries.includes(`${id}-launch-failed.json`) ? 'launch-failed' : 'outcome-unknown',
|
|
79
94
|
...(entries.includes(`${id}.json`) ? { reportPath: path.join(directory, `${id}.json`) } : {}) };
|
|
80
95
|
});
|
|
81
|
-
return { status: records.proposal ? 'awaiting-cloud-confirmation' : records.research ? 'awaiting-
|
|
96
|
+
return { status: records.proposal ? 'awaiting-cloud-confirmation' : records.research ? 'awaiting-design-proposal' : 'awaiting-research', records, attempts,
|
|
97
|
+
instruction: 'Reuse original valid images, receipts and approved designs. A failed submission, refreshed research or new conversation does not require image generation.', grantsWriteAuthority: false };
|
|
82
98
|
}
|
|
83
99
|
export const DESIGN_LOCAL_OPERATIONS = Object.freeze({ 'design-browser-info': readDesignBrowserIdentity, 'design-flow': recordDesignFlow, 'design-research': recordDesignResearch,
|
|
84
100
|
'design-propose': proposeDesign, 'design-status': readDesignStatus, 'design-verify': verifyDesignBrowser });
|
package/design-store.mjs
CHANGED
|
@@ -17,10 +17,18 @@ export async function designDirectory(input) {
|
|
|
17
17
|
}
|
|
18
18
|
export async function designSave(directory, name, value) {
|
|
19
19
|
const file = path.join(directory, name);
|
|
20
|
-
await atomicMutationJson14(file, value);
|
|
20
|
+
try { await atomicMutationJson14(file, value); }
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (!['EEXIST', 'ENTROROVE_STATE_EXISTS'].includes(error.code)) throw error;
|
|
23
|
+
if (canonical(await readMutationJson14(file)) !== canonical(value)) designFail('ARTIFACT_CONFLICT', 'This design revision already contains different evidence. Reuse its original records or create a revision for actual design changes; do not regenerate images to recover a failed submission');
|
|
24
|
+
}
|
|
21
25
|
return file;
|
|
22
26
|
}
|
|
23
27
|
export async function designLoad(directory, name) { return readMutationJson14(path.join(directory, name)); }
|
|
28
|
+
export async function designExisting(directory, name) {
|
|
29
|
+
try { return await designLoad(directory, name); }
|
|
30
|
+
catch (error) { if (error.code === 'ENOENT') return null; throw error; }
|
|
31
|
+
}
|
|
24
32
|
export async function designArtifact(file, expected) {
|
|
25
33
|
const { bytes, stat } = await regular(file, 8_388_608);
|
|
26
34
|
if (stat.nlink !== 1 || stat.mode & 0o111 || expected && (sha256(bytes) !== expected.sha256 || bytes.length !== expected.bytes)) designFail('ARTIFACT_CHANGED', 'Design evidence must retain its exact non-executable, single-link bytes');
|
|
@@ -28,9 +36,33 @@ export async function designArtifact(file, expected) {
|
|
|
28
36
|
}
|
|
29
37
|
export async function designCopy(directory, name, file) {
|
|
30
38
|
const bytes = await designArtifact(file), target = path.join(directory, name);
|
|
31
|
-
await
|
|
39
|
+
await designWrite(target, bytes);
|
|
32
40
|
return { path: target, sha256: sha256(bytes), bytes: bytes.length };
|
|
33
41
|
}
|
|
42
|
+
export async function designWrite(file, bytes) {
|
|
43
|
+
try { await writeMutationFile14(file, bytes); }
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code !== 'EEXIST') throw error;
|
|
46
|
+
if (!(await designArtifact(file)).equals(bytes)) designFail('ARTIFACT_CONFLICT', 'Existing design evidence has different bytes. Preserve it and reuse the original image/receipt, or choose a new revision only for actual design changes');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export async function assertDesignGeneration(mockup, recordedAt) {
|
|
50
|
+
if (typeof mockup?.image?.path !== 'string' || typeof mockup?.generation?.receipt?.path !== 'string'
|
|
51
|
+
|| typeof mockup?.generation?.tool !== 'string' || typeof mockup?.generation?.requestId !== 'string'
|
|
52
|
+
|| !/^[a-f0-9]{64}$/.test(mockup?.generation?.promptSha256 ?? '')) designFail('GENERATION', 'Each mockup requires image.path, generation.receipt.path, generation.tool, generation.requestId and generation.promptSha256 from its original tool result');
|
|
53
|
+
const image = await designArtifact(mockup.image.path, mockup.image.sha256 ? mockup.image : undefined);
|
|
54
|
+
designPng(image);
|
|
55
|
+
let receipt;
|
|
56
|
+
try { receipt = JSON.parse((await designArtifact(mockup.generation.receipt.path,
|
|
57
|
+
mockup.generation.receipt.sha256 ? mockup.generation.receipt : undefined)).toString('utf8')); }
|
|
58
|
+
catch { designFail('GENERATION', 'Provide the original readable image generation receipt; do not regenerate an existing image to repair its receipt path'); }
|
|
59
|
+
if (!receipt || typeof receipt !== 'object' || Array.isArray(receipt)
|
|
60
|
+
|| Object.keys(receipt).sort().join('|') !== 'generatedAt|imageSha256|promptSha256|requestId|schemaVersion|tool') designFail('GENERATION', 'Image generation records require schemaVersion, tool, requestId, promptSha256, imageSha256 and the actual generatedAt');
|
|
61
|
+
if (receipt.schemaVersion !== 'entrorove.image-generation-record/1.0' || receipt.tool !== mockup.generation.tool
|
|
62
|
+
|| receipt.requestId !== mockup.generation.requestId || receipt.promptSha256 !== mockup.generation.promptSha256
|
|
63
|
+
|| receipt.imageSha256 !== sha256(image) || !Number.isFinite(Date.parse(receipt.generatedAt))
|
|
64
|
+
|| Date.parse(receipt.generatedAt) > Date.parse(recordedAt)) designFail('GENERATION', 'The original tool, requestId, prompt hash, image hash or generatedAt does not match. Correct that binding without changing the actual generation time or regenerating the image');
|
|
65
|
+
}
|
|
34
66
|
function crc32(bytes) {
|
|
35
67
|
let crc = 0xffffffff;
|
|
36
68
|
for (const byte of bytes) { crc ^= byte; for (let i = 0; i < 8; i++) crc = crc & 1 ? 0xedb88320 ^ crc >>> 1 : crc >>> 1; }
|
|
@@ -76,18 +108,7 @@ export async function assertDesignArtifacts(policy, input) {
|
|
|
76
108
|
if (source.original.path !== path.join(directory, `source-${index}.original`)) designFail('ARTIFACT_LOCATION', 'Original UI bytes must remain in their approved design revision');
|
|
77
109
|
await designArtifact(source.original.path, source.original);
|
|
78
110
|
}
|
|
79
|
-
for (const mockup of policy.mockups)
|
|
80
|
-
designPng(await designArtifact(mockup.image.path, mockup.image));
|
|
81
|
-
let receipt;
|
|
82
|
-
try { receipt = JSON.parse((await designArtifact(mockup.generation.receipt.path, mockup.generation.receipt)).toString('utf8')); }
|
|
83
|
-
catch { designFail('GENERATION', 'The original image generation record is unavailable or invalid'); }
|
|
84
|
-
if (Object.keys(receipt).sort().join('|') !== 'generatedAt|imageSha256|promptSha256|requestId|schemaVersion|tool') designFail('GENERATION', 'Image generation records contain only tool identity, prompt/image hashes and the actual generation time');
|
|
85
|
-
if (receipt.schemaVersion !== 'entrorove.image-generation-record/1.0' || receipt.tool !== mockup.generation.tool
|
|
86
|
-
|| receipt.requestId !== mockup.generation.requestId || receipt.promptSha256 !== mockup.generation.promptSha256
|
|
87
|
-
|| receipt.imageSha256 !== mockup.image.sha256 || !Number.isFinite(Date.parse(receipt.generatedAt))
|
|
88
|
-
|| Date.parse(receipt.generatedAt) < Date.parse(policy.researchRecordedAt)
|
|
89
|
-
|| Date.parse(receipt.generatedAt) > Date.parse(policy.mockupsRecordedAt)) designFail('GENERATION', 'Record the actual image/design tool result after research, bound to these image bytes');
|
|
90
|
-
}
|
|
111
|
+
for (const mockup of policy.mockups) await assertDesignGeneration(mockup, policy.mockupsRecordedAt);
|
|
91
112
|
}
|
|
92
113
|
export async function captureDesignSourceBaseline(input, directory, names) {
|
|
93
114
|
const baseline = [];
|
|
@@ -96,7 +117,7 @@ export async function captureDesignSourceBaseline(input, directory, names) {
|
|
|
96
117
|
if (!file.exists) { baseline.push({ path: name, original: null }); continue; }
|
|
97
118
|
const { content } = await mutationRead14(input, input.repositoryRoot, name), target = path.join(directory, `source-${index}.original`);
|
|
98
119
|
if (content.length > 2_097_152) designFail('LINT_LIMIT', 'Split approved UI source inspection before capturing a source larger than 2 MiB');
|
|
99
|
-
await
|
|
120
|
+
await designWrite(target, content);
|
|
100
121
|
baseline.push({ path: name, original: { path: target, sha256: sha256(content), bytes: content.length } });
|
|
101
122
|
}
|
|
102
123
|
return baseline;
|
package/manifest.json
CHANGED
package/package.json
CHANGED
package/release-state.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "entrorove.release-state/1.0",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"digest": "
|
|
3
|
+
"version": "0.1.85",
|
|
4
|
+
"digest": "e27fa2e962649794d57c6786abbbe07755e85e3700bb1e18d806092248e568cb",
|
|
5
5
|
"files": {
|
|
6
6
|
"local-recovery-budget.mjs": "be3af07db8e4cc96782edb130e827039c7a99bbe8275a92f0f741533716017f3",
|
|
7
7
|
"remote-capability-cache.mjs": "9c22067e1a21eb2a209747a8ab2f27c0496b14a22a7e0f5654840e2035148df0",
|
|
@@ -107,24 +107,24 @@
|
|
|
107
107
|
"design-browser.mjs": "ced192caefabef6001c27bc15f33b265e326a57271384952e2729cdfec64b722",
|
|
108
108
|
"design-context.mjs": "2e0d764a2a55d05e00477f984135d77808307afb1fc8668f35910612b5e4cea5",
|
|
109
109
|
"design-contracts.d.mts": "af1a5740557d2c8ce90702179e78797a618544c89dbbb3962cf92bef24f9c2b8",
|
|
110
|
-
"design-contracts.mjs": "
|
|
110
|
+
"design-contracts.mjs": "ba389eca12ff85c26ae7acba26eb0a15f586c9af04b2c3785e1237f2f8dc43ea",
|
|
111
111
|
"design-declaration.mjs": "4c4be3731bcf169e15a100c5261a34686ddd43b780dfa8667a8d7c36766e5237",
|
|
112
112
|
"design-frameworks.mjs": "57c316420269d3b46044f14306c5f807e0e87d5a527b3c9995a7066b2b3da952",
|
|
113
113
|
"design-gate.mjs": "7d06a7065951f97b6abc10bba4bb517200c3d0b2720354b878f98f5cdeab3b19",
|
|
114
114
|
"design-lint.mjs": "76adb33d0faf0cfcdef7583a2f4835eccc7e49811480e2fd2b09779a81c0cbab",
|
|
115
115
|
"design-local-contracts.mjs": "f331bd1d21f30994596bd9c46008e551f3ed2b6d5eea19d4546c7da0663b768a",
|
|
116
|
-
"design-operations.mjs": "
|
|
116
|
+
"design-operations.mjs": "dfad99258e016fa55a783551ae968915405599207b2407e04e2daf637b6b79d6",
|
|
117
117
|
"design-policy.d.mts": "48fed97fca0f97fef786f182f666d7e6e33efb814cda1991239e1c38984cf80e",
|
|
118
118
|
"design-policy.mjs": "6ca60298777667502616eaad4aff92b92947ee1538f69be9e398be076367c759",
|
|
119
119
|
"design-research.mjs": "6cdc5d6ab96e47dd3a3abc4f31c3bc800a469ce1522b7e49aa73eb86d44b988d",
|
|
120
120
|
"design-research-network.mjs": "7ed4c670eff767b6b4783d032b4fea14f9a4aeed15c1340451967dead7ce8e2f",
|
|
121
121
|
"design-runner.mjs": "2aaeb4a2e6034b7566943719cfef1b78dbb8f5d3a812d155a8a328ded4340797",
|
|
122
|
-
"design-store.mjs": "
|
|
122
|
+
"design-store.mjs": "475c6d7b682e348d6d3a316f8e7454099c8a234375bd4336e2a6bcecfdd28fec",
|
|
123
123
|
"design-stage.mjs": "545a9f2e027b2333ffb09df4516f6d3866d9585a19a43a39dbff16145dc75bca",
|
|
124
124
|
"design-stage-contracts.mjs": "f4242876fd6472096dd22609868e955a54c9269d6edd7bff399c6083ece52848",
|
|
125
125
|
"design-storage.mjs": "af02cf3fb59f9ef7c7d6276b43e6a53e3d305a77492e3cb824dd05d52adc2fd3",
|
|
126
126
|
"design-ui.mjs": "6dd3c5a97ec67e8f1c23e57bcab5eb64380c520befada6f264c4bfcf6b11c9f7",
|
|
127
|
-
"DESIGN-GATES.md": "
|
|
127
|
+
"DESIGN-GATES.md": "4169feafef148a459a612a3d1161429b3b4e243739e40aa47e2a4ebd0958a8b1",
|
|
128
128
|
"DESIGN-STAGED-ACCEPTANCE.md": "90ee32e08fa4bfd56fcb8f9ffb19f6a10217203448b6676bcf58c4ea6e03ba6c",
|
|
129
129
|
"design-release.mjs": "03863c95bec899782c1bed09c14f0e302ec2ec7bc221a36a4f7c91e501734b72",
|
|
130
130
|
"local-final-gate16.mjs": "083aec470670e6cdb7ad2ab27b7a51b53a9ec3a8b4121cb9cbfa17d9940892c2",
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
"install-cleanup.mjs": "8a8e1a7dfe37427f86cd4d6e317890d0829894a9949d4216674a716b36edbc54",
|
|
175
175
|
"review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
|
|
176
176
|
"version.mjs": "85284d4cd5e3314b449e6ae124243f96397bcc274817b5983a79cfdd903d46fd",
|
|
177
|
-
"SKILL.md": "
|
|
177
|
+
"SKILL.md": "838bae0f55462486b291557a7633c309230d7c1645504840c27c42d3d4edb7f6",
|
|
178
178
|
"CAPABILITY-MAP.md": "57805ba6a4b4fc79ffadb3bd1436999f82c295feac7bdc230bdc123f67662546",
|
|
179
179
|
"AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
|
|
180
180
|
"DELIVERY-BOUNDARIES.md": "cb23ce71b2197085c8441ba7c5b6c05fb21f2a954bf66b4d60cfef8b15895b8b",
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
"local-scope-state.mjs": "7a6d275d6267a4d0bb55582e1bcc7654c502ecba9221b84b2149a98f0ee7518f",
|
|
258
258
|
"local-scope-workspaces.mjs": "8a75ce7b6639b5682370b5738c5c2a90408e3130729608e008f03053cd50351f",
|
|
259
259
|
"local-task-routing.mjs": "6e2e506b2fb0e2e8d8c5147d7df32849b57a4e94a23634f01a355d45735858e4",
|
|
260
|
-
"remote-client.mjs": "
|
|
260
|
+
"remote-client.mjs": "5f74be6d699c35f9ac3f251adaff682c1b736b47561f40d695a5ce9f94a8a2c9",
|
|
261
261
|
"service-prerequisite.mjs": "7ac5a66d8b423941a5a2cef3aecce18a681aaf2b1a61612fecfa17f5d99f302d",
|
|
262
262
|
"local-source-execution.mjs": "88f6ce1ea50d0a7b92e0cb47d535f8140491c52d9c6c0dfddf91ea90397a36f5",
|
|
263
263
|
"fs-request-trace.mjs": "972eacbe21537bc102047127327449a6a4b0f0c65d75e3472370de91dedf4d2f",
|
package/releases.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.1.85",
|
|
4
|
+
"date": "2026-10-23",
|
|
5
|
+
"zh": "修复设计证据失败恢复导致重复生成图片;复用原图与真实生成记录,补齐 CSS 自定义属性支持。执行计划补齐原始合约绑定和 Blueprint 参数约束,发送前指出缺失字段并禁止无效原样重试。",
|
|
6
|
+
"en": "Reuse original design images and truthful receipts after failed submissions, and support CSS custom properties. Expose workflow plan binding and raw Blueprint constraints, report missing payload fields before transmission, and prevent unchanged invalid retries."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.1.84",
|
|
4
10
|
"date": "2026-10-23",
|
package/remote-client.mjs
CHANGED
|
@@ -113,9 +113,7 @@ async function invokeDiscovered(data, token, { home, apiOrigin }) {
|
|
|
113
113
|
feedbackRequired = requiresAutomaticFeedback(data.operation,data.input,capabilities);
|
|
114
114
|
const schema = capabilities?.operationSchemas?.[data.operation];
|
|
115
115
|
if (!capabilities?.operations?.includes(data.operation) || !schema) throw new Error(`Capability discovery does not support ${data.operation}`);
|
|
116
|
-
|
|
117
|
-
if (!validate(data.input)) throw new Error(`Invalid ${data.operation} input: ${validate.errors.map(error => `${error.instancePath || '/'} ${error.message}`).join('; ')}`);
|
|
118
|
-
validateDiscoveredChecks(data.input, capabilities.inputChecks?.[data.operation]);
|
|
116
|
+
validateMachineCapability(data.operation, data.input, capabilities);
|
|
119
117
|
}
|
|
120
118
|
return postInvocation(call, token, home,feedbackRequired);
|
|
121
119
|
}
|
|
@@ -172,6 +170,8 @@ export async function postInvocation(call, token, home, feedbackRequired = false
|
|
|
172
170
|
await logged({ event: response.status >= 500 ? 'outcome-unknown' : 'rejected' });
|
|
173
171
|
if (response.status >= 500) throw unknownInvocation(call.requestId, { httpStatus: response.status });
|
|
174
172
|
const code = typeof result?.error === 'string' && /^[a-z][a-z0-9-]{0,63}$/.test(result.error) ? result.error : 'request-rejected';
|
|
173
|
+
if (result?.code === 'ENTROROVE_INPUT_INVALID') throw Object.assign(new Error(`Invalid invocation input; ${result.issues?.map(issue => `${issue.path.join('.')} ${issue.message}`).join('; ') ?? 'consult the discovered payload schema'}. Do not retry unchanged input; requestId=${call.requestId}`),
|
|
174
|
+
{ code: result.code, issues: result.issues, nextAction: result.nextAction, automaticRetryAllowed: false });
|
|
175
175
|
throw new Error(`entrorove invocation failed: HTTP ${response.status}; ${code}; requestId=${call.requestId}`);
|
|
176
176
|
}
|
|
177
177
|
if (!result?.output || result.output.requestId !== call.requestId) {
|
|
@@ -219,6 +219,20 @@ function validateMachineCapability(operation, input, capabilities) {
|
|
|
219
219
|
const validate = discoveredAjv().compile(schema);
|
|
220
220
|
if (!validate(input)) throw new Error(`Invalid ${operation} input: ${validate.errors.map(error => `${error.instancePath || '/'} ${error.message}`).join('; ')}`);
|
|
221
221
|
validateDiscoveredChecks(input, capabilities.inputChecks?.[operation]);
|
|
222
|
+
if (operation === 'continue') {
|
|
223
|
+
const payloadSchema = capabilities.workflowOperations?.operationSchemas?.[input.operation];
|
|
224
|
+
if (!payloadSchema) {
|
|
225
|
+
if (input.operation === 'workflow-plan') throw new Error('Capability discovery omitted workflow-plan payload schema; refresh discovery before sending. Do not retry unchanged input.');
|
|
226
|
+
return; // Native continuation schemas remain owned by their original runtime.
|
|
227
|
+
}
|
|
228
|
+
const payload = discoveredAjv().compile(payloadSchema);
|
|
229
|
+
if (!payload(input.payload)) {
|
|
230
|
+
const issues = payload.errors.slice(0, 32).map(error => ({ path: error.instancePath || '/', code: error.keyword,
|
|
231
|
+
message: error.message, ...(error.keyword === 'required' ? { missingField: error.params.missingProperty } : {}) }));
|
|
232
|
+
throw Object.assign(new Error(`Invalid ${input.operation} payload before transmission: ${issues.map(issue => `${issue.path} ${issue.message}${issue.missingField ? ` (${issue.missingField})` : ''}`).join('; ')}. Correct these fields; do not retry unchanged input.`),
|
|
233
|
+
{ code: 'ENTROROVE_INPUT_INVALID', issues, automaticRetryAllowed: false, knownBeforeEffects: true });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
222
236
|
}
|
|
223
237
|
function stoppedOriginalResume(response, output, operation, input) {
|
|
224
238
|
return operation === 'task-resume' && response.status === 422 && output?.schemaVersion === 'entrorove.skill.response/1.0'
|
|
@@ -244,7 +258,9 @@ export function createTaskMessageClient(dependencies) {
|
|
|
244
258
|
presentInvocationNotices(result,dependencies.onIdeEvent);
|
|
245
259
|
if (stoppedOriginalResume(response, result.output, operation, input)) return result.output;
|
|
246
260
|
if (!response.ok || ['blocked', 'failed', 'incomplete'].includes(result.output.status)) {
|
|
247
|
-
|
|
261
|
+
const detail = result.output.code === 'ENTROROVE_INPUT_INVALID' ? `; ${result.output.issues?.map(issue => `${issue.path.join('.')} ${issue.message}`).join('; ') ?? 'correct the operation payload'}; do not retry unchanged input` : '';
|
|
262
|
+
throw Object.assign(new Error(`Task message invocation was rejected: HTTP ${response.status}; requestId=${original.requestId}${detail}`),
|
|
263
|
+
{ code: result.output.code, issues: result.output.issues, automaticRetryAllowed: result.output.automaticRetryAllowed });
|
|
248
264
|
}
|
|
249
265
|
return result.output;
|
|
250
266
|
},
|