@entrorove/skill 0.1.73 → 0.1.78
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/CAPABILITY-MAP.md +5 -0
- package/CHANGELOG.md +37 -0
- package/DESIGN-GATES.md +5 -9
- package/DESIGN-STAGED-ACCEPTANCE.md +103 -0
- package/IDE-EXECUTION.md +21 -4
- package/REUSE-DISCIPLINE.md +3 -3
- package/SKILL.md +14 -4
- package/cli.mjs +8 -4
- package/cloud-mutation-authority.mjs +8 -2
- package/cloud-task-client.mjs +10 -0
- package/cloud-watch-client.mjs +8 -2
- package/design-browser-checks.mjs +18 -5
- package/design-browser-pages.mjs +96 -0
- package/design-browser-state.mjs +1 -0
- package/design-browser.mjs +36 -40
- package/design-contracts.mjs +4 -1
- package/design-declaration.mjs +3 -0
- package/design-frameworks.mjs +2 -1
- package/design-gate.mjs +3 -0
- package/design-operations.mjs +4 -1
- package/design-policy.mjs +12 -4
- package/design-release.mjs +6 -0
- package/design-research-network.mjs +82 -0
- package/design-research.mjs +8 -25
- package/design-runner.mjs +3 -0
- package/design-stage-contracts.mjs +12 -0
- package/design-stage.mjs +78 -0
- package/design-storage.mjs +63 -0
- package/fs-request-trace.mjs +67 -0
- package/local-contracts.mjs +1 -1
- package/local-discipline-repository.mjs +4 -2
- package/local-invocations.mjs +6 -0
- package/local-mutation-files.mjs +13 -5
- package/local-probe.mjs +8 -4
- package/local-recovery-budget.mjs +31 -0
- package/local-scope-repository.mjs +18 -3
- package/local-scope-signature.mjs +2 -2
- package/local-source-execution.mjs +47 -0
- package/manifest.json +1 -1
- package/mutation-challenge-refresh.mjs +3 -0
- package/mutation-client.mjs +17 -5
- package/native-client.mjs +4 -1
- package/package.json +14 -1
- package/release-state.json +63 -50
- package/release.mjs +6 -1
- package/releases.json +30 -0
- package/remote-capability-cache.mjs +20 -0
- package/remote-client.mjs +24 -4
- package/scope-amendment-client.mjs +62 -6
- package/scope-amendment-observer.mjs +9 -3
- package/scope-amendment-remote.mjs +20 -6
- package/scope-amendment-retirement.mjs +4 -2
- package/scope-amendment-state.mjs +9 -5
- package/scope-context-client.mjs +7 -0
- package/scope-context-journal.mjs +8 -2
- package/scope-context-remote.mjs +16 -5
- package/scope-evidence-remote.mjs +16 -7
- package/scope-runner-client.mjs +34 -7
- package/scope-runner-journal.mjs +82 -5
- package/scope-write-observation.d.mts +4 -0
- package/scope-write-observation.mjs +43 -0
- package/service-prerequisite.mjs +87 -0
- package/task-entry-client.mjs +3 -0
- package/workflow-observation-client.mjs +2 -0
- package/workflow-observation-remote.mjs +13 -5
package/CAPABILITY-MAP.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# 能力对账图(WP-02 / AC-20)
|
|
2
2
|
|
|
3
|
+
## Task entrance authorization and finite handoff recovery
|
|
4
|
+
|
|
5
|
+
New tasks declare their concrete `task.authorization` boundary at entrance and obtain one original-user approval. Covered design/role/context/discipline/visual decisions reuse its audit automatically; uncovered permissions remain explicit. Planning reuses timely admitted signed evidence while current mutation checks remain fresh. Completed native skill results survive interrupted handoffs. SDK/API recovery is durably limited to two retries, with GET-only recovery of unknown results; exhaustion preserves the original state. See [IDE-EXECUTION.md](IDE-EXECUTION.md#one-task-entrance-approval). This is local source behavior until deployed; it does not claim that old tasks have a new authorization or that an installation/publication occurred.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
中英文退出治理声明:`entrorove exit` 或当前会话的实际用户明确退出原文,只结束本轮治理,保留源码、原清单顺序、审批及全部失败记录;不取消或完成剩余业务,不从翻译问题、引用、截图、条件句或报错推断退出。英文示例:`From now on, do not use the skill to execute the remaining tasks.`
|
|
4
9
|
|
|
5
10
|
直接 SDK 会话缺少 IDE 记录时,`exit` 仅为当前已支持主机/会话和实际目录保存 inactive/user-exited 标记,不创建 flow、任务、审批或回执。已有原目录绑定及历史保留;损坏、不匹配或不安全的记录仍明确拒绝覆盖。
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.78 — 2026-10-21
|
|
4
|
+
|
|
5
|
+
- 完整交付原八技能的一次入口授权、范围内衔接与有界恢复;修复已实际取消的弹窗先于点击回执关闭时的原窗口接续,不重发点击,原父窗口、精确路由、安全签名、快照及回执保护保持。
|
|
6
|
+
- Deliver the original eight skills with one task entrance approval, scoped handoffs and bounded recovery. Preserve the original opener when an actual popup cancellation closes before its click acknowledgement, without replaying the click or weakening exact routes, signatures, snapshots or receipts.
|
|
7
|
+
|
|
8
|
+
## 0.1.77 — 2026-10-21
|
|
9
|
+
|
|
10
|
+
- 未发布候选,正式内容并入 0.1.78。
|
|
11
|
+
- Unpublished candidate; incorporated into 0.1.78.
|
|
12
|
+
|
|
13
|
+
- 正式发布八技能一次入口授权、范围内衔接和有界恢复,保留原八技能核心、安全签名、快照及回执;兼容旧范围续签,并验证已发现能力清单复用时仍拒绝非法输入。
|
|
14
|
+
- Publish the complete original eight skills with one task entrance approval, scoped handoffs and bounded recovery, preserving native cores, signatures, snapshots and receipts; retain legacy scope renewal compatibility and verify invalid input rejection with reused capability discovery.
|
|
15
|
+
|
|
16
|
+
## 0.1.76 — 2026-10-21
|
|
17
|
+
|
|
18
|
+
- 未发布候选,正式内容并入 0.1.78。
|
|
19
|
+
- Unpublished candidate; incorporated into 0.1.78.
|
|
20
|
+
|
|
21
|
+
- 正式交付八技能的一次入口授权、范围内衔接与有界恢复;兼容旧凭据续签的独立写入计量,保留签名、快照、回执和原批准范围校验。
|
|
22
|
+
- Ship the eight-skill single-entrance authorization, scoped handoff and bounded recovery; preserve signed write accounting across legacy proof renewal without changing approved scope, signatures, snapshots or receipt checks.
|
|
23
|
+
|
|
24
|
+
## 0.1.75 — 2026-10-21
|
|
25
|
+
|
|
26
|
+
- 未发布候选,正式内容并入 0.1.78。
|
|
27
|
+
- Unpublished candidate; incorporated into 0.1.78.
|
|
28
|
+
|
|
29
|
+
- 正式发布复用已验证的八技能任务衔接与恢复改进;移除发布测试中未发布法律模块的夹具耦合。八技能核心实现保持原版。
|
|
30
|
+
- Release the verified eight-skill task handoff and recovery improvements; remove unreleased legal-module coupling from release test fixtures. The eight original skill cores remain unchanged.
|
|
31
|
+
|
|
32
|
+
## 0.1.74 — 2026-10-21
|
|
33
|
+
|
|
34
|
+
- 未发布候选,正式内容并入 0.1.78。
|
|
35
|
+
- Unpublished candidate; incorporated into 0.1.78.
|
|
36
|
+
|
|
37
|
+
- 八技能衔接共用一次明确的任务入口批准,范围内复用原审批与已完成计算;修复范围状态读取、元数据扫描、独立可写计量及带查询参数/弹窗的设计验收。恢复次数持久有界,合法前置进度可继续,保留签名、快照、回执与未知结果只查询的保护,禁止跨项目耦合。
|
|
38
|
+
- Reuse one explicit task entrance approval and completed skill computations within the approved boundary. Fix scope state reads, metadata scanning, independently signed write-role metering and query/popup design acceptance. Bound recovery durably while allowing verified prerequisite progress, preserving signatures, snapshots, receipts, GET-only unknown recovery and project isolation.
|
|
39
|
+
|
|
3
40
|
## 0.1.73 — 2026-10-20
|
|
4
41
|
|
|
5
42
|
- 修复通用范围连接在长时间扫描后的签名期限绑定:原 S 证明到期时间不晚于原云端挑战,保留原随机数、完整范围、独立验签和失败历史;已过期挑战仍明确拒绝。
|
package/DESIGN-GATES.md
CHANGED
|
@@ -39,7 +39,7 @@ never silently relabel them as a supported framework.
|
|
|
39
39
|
References are bounded, credential-free public reads. They grant no business
|
|
40
40
|
source, account, database, runtime, deployment or write access.
|
|
41
41
|
|
|
42
|
-
## 3. Create mockups or prototypes
|
|
42
|
+
## 3. Create mockups or prototypes under the original task authorization
|
|
43
43
|
|
|
44
44
|
Respect the user's choice: an effect mockup (`kind: mockup`) or a prototype /
|
|
45
45
|
wireframe (`kind: prototype`) is a valid visual baseline. Use the host's actual
|
|
@@ -76,16 +76,13 @@ layout bounds, computed styles and a success-message selector.
|
|
|
76
76
|
|
|
77
77
|
Show the flow, research links, images and concrete rules to the user. Submit
|
|
78
78
|
`continue` → `workflow-design-review` with the exact `design`. Present the
|
|
79
|
-
returned
|
|
80
|
-
actual answer to its original callback. No default, timeout, remembered answer,
|
|
81
|
-
model-generated `approved:true` or another account can approve it. Do not
|
|
82
|
-
prepare UI implementation code before this confirmation.
|
|
79
|
+
returned decision. If the task entrance explicitly delegated interface design within these paths and requirements, `approvalReused: true` continues without a new question. Otherwise present the concrete uncovered design decision using the host question UI and submit only the user's actual answer to its callback. No default, timeout, caller `approved:true` or foreign account can approve it. Implementation requires the recorded original authorization or actual additional confirmation.
|
|
83
80
|
|
|
84
81
|
The response's `approvedDesign` contains `design` and `designApproval`. Carry
|
|
85
82
|
both into the original `policy.discipline`. This approval grants no execution,
|
|
86
83
|
scope or write authority. Original scope, candidate source hashes, signed plan,
|
|
87
84
|
budget, mutation pass and any separate sensitive-change approval still apply.
|
|
88
|
-
Changing the design
|
|
85
|
+
Changing the design records a new revision and rechecks the same task authorization; request a new decision only for a concrete change outside it. Keep every
|
|
89
86
|
old proposal, rejection and approval. An existing planned task may instead use
|
|
90
87
|
the combined `workflow-discipline-review`, with the same original proof checks.
|
|
91
88
|
|
|
@@ -134,8 +131,7 @@ Keep preview requests restricted to isolated authorized fixture data.
|
|
|
134
131
|
|
|
135
132
|
The result is `needs-visual-confirmation`, never an aesthetic pass. Show real
|
|
136
133
|
screenshots alongside approved mockups, then submit the exact report through
|
|
137
|
-
`continue` → `workflow-design-acceptance
|
|
138
|
-
visual answer. Final verdict rechecks original approval, tests and source
|
|
134
|
+
`continue` → `workflow-design-acceptance`. An entrance authorization explicitly delegating visual acceptance can be reused after the actual source-bound functional/browser checks pass. Otherwise obtain the original user's actual visual answer. A reused decision is not a claim that the user personally inspected the screenshots. Final verdict rechecks original approval, tests and source
|
|
139
135
|
bindings. Later source/test changes require fresh acceptance.
|
|
140
136
|
|
|
141
137
|
## Failure and enforcement boundaries
|
|
@@ -149,7 +145,7 @@ historical missing process results.
|
|
|
149
145
|
Static rules cover the implemented HTML/JS/TS/JSX/Vue/Svelte and CSS subset.
|
|
150
146
|
Unsupported preprocessors/native UI syntax fail explicitly; official research
|
|
151
147
|
does not prove every framework API or language scenario was verified. Browser
|
|
152
|
-
checks
|
|
148
|
+
checks remain necessary; visual judgment follows the original user's retained or explicitly delegated acceptance boundary.
|
|
153
149
|
|
|
154
150
|
SDK checks govern SDK operations. Optional host hooks can add interception, but
|
|
155
151
|
neither a hook nor a generic process runner is an OS sandbox. A generic runner's
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Staged acceptance and navigation handoffs
|
|
2
|
+
|
|
3
|
+
This reference describes local source support. It does not grant execution,
|
|
4
|
+
write, account, deployment, or business completion authority. Preserve the
|
|
5
|
+
original flow, task, scope, approvals, source hashes, and every prior attempt.
|
|
6
|
+
Discover the actual schemas through `local capabilities` before use.
|
|
7
|
+
|
|
8
|
+
## Compatibility
|
|
9
|
+
|
|
10
|
+
The default contract continues to require whole-site functional coverage.
|
|
11
|
+
An existing-schema navigation handoff uses `step.readBack` with
|
|
12
|
+
`kind: "local-storage"`, an exact `key`, complete JSON `before`/`after` objects
|
|
13
|
+
or `null`, and `afterReload` assertions. It applies only to an actual `open`
|
|
14
|
+
or `navigate` edge. The local runner verifies the entire JSON value, including
|
|
15
|
+
its absence, without promoting private handoff fields into visible UI fields.
|
|
16
|
+
It checks one matching storage operation and all other storage keys remain
|
|
17
|
+
unchanged. It never permits HTTP mutation or a commit-success message.
|
|
18
|
+
|
|
19
|
+
This representation uses existing schema fields. It does not remove the
|
|
20
|
+
previous server's whole-site coverage requirement. A save/delete retains its
|
|
21
|
+
original persistence proof and explicit action requirements.
|
|
22
|
+
Compatibility of these fields alone does not prove that an original project
|
|
23
|
+
can pass an older server's other validators. In particular, the original
|
|
24
|
+
0.1.73 validator treats multiple workspace versions of one framework as a
|
|
25
|
+
conflict. Keep actual workspace bindings and report that separate dependency;
|
|
26
|
+
do not remove a real framework to make a proposal fit the old validator.
|
|
27
|
+
|
|
28
|
+
The new `rules.stage` and `step.storageHandoffs` fields require the same shared
|
|
29
|
+
validator on the original server. An older strict server rejects them. Do not
|
|
30
|
+
submit a known unsupported request, remove the fields before signing, fabricate
|
|
31
|
+
approval, or silently bypass server validation. A local source update does not
|
|
32
|
+
deploy server code. Preserve any deployment restriction and report this
|
|
33
|
+
dependency explicitly.
|
|
34
|
+
|
|
35
|
+
## A stage within the original whole site
|
|
36
|
+
|
|
37
|
+
`rules.stage` contains:
|
|
38
|
+
|
|
39
|
+
- `id`: the immutable stage identity.
|
|
40
|
+
- `requirementIds`: references to the original site's requirements.
|
|
41
|
+
- `pageIds`: all executable pages for this stage.
|
|
42
|
+
- `edgeIds`: all executable original edges, with both endpoints executable.
|
|
43
|
+
- `readOnlyPages`: every other original page, each with `pageId` and nonempty
|
|
44
|
+
`sources` entries containing exact existing `path` and `sha256` values.
|
|
45
|
+
|
|
46
|
+
Every original page belongs to exactly one group. Keep the full original
|
|
47
|
+
`site.pages`, `site.edges`, roles, public/internal fields, and task scope.
|
|
48
|
+
Read-only source paths must remain in `verificationPaths`. Their actual hashes
|
|
49
|
+
are checked at proposal, guarded writes, runner boundaries, and before/after
|
|
50
|
+
browser acceptance. They cannot be overwritten, removed, moved, or relabeled
|
|
51
|
+
as writable merely because they also appear in `uiPaths`.
|
|
52
|
+
|
|
53
|
+
Every executable page/state/theme needs its original mockup or prototype and
|
|
54
|
+
one scenario. Every executable edge must be tested. Context-only mockups may
|
|
55
|
+
remain in the proposal without executable scenarios; optional context browser
|
|
56
|
+
scenarios must contain no interaction steps. A source-bound context baseline
|
|
57
|
+
is not proof that its deferred interactions ran or passed.
|
|
58
|
+
|
|
59
|
+
Retain the stage and its read-only source bindings under the original task authorization. Reuse an entrance approval that explicitly covers the design decision; present a new question only for a concrete permission outside that boundary. Keep genuine visual acceptance after
|
|
60
|
+
real tests. Returned `acceptanceScope` identifies the limited coverage and
|
|
61
|
+
always has `claimsWholeBusinessCompletion: false`. Later stages require their
|
|
62
|
+
own truthful baseline and original approvals. They cannot reuse acceptance as
|
|
63
|
+
new write authority.
|
|
64
|
+
|
|
65
|
+
## Exact navigation storage declarations
|
|
66
|
+
|
|
67
|
+
For multiple keys or session storage, declare `step.storageHandoffs`, a list of
|
|
68
|
+
at most eight entries:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{"area":"local-storage","key":"navigationDraft","method":"setItem","before":null,"after":"{\"itemIds\":[7]}","count":1}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The area is `local-storage` or `session-storage`. The method is `setItem` or
|
|
75
|
+
`removeItem`; removal has `after: null`. Values are exact raw strings or null,
|
|
76
|
+
bounded to 8192 characters. Keys are unique within an action. Identical
|
|
77
|
+
before/after values can describe an idempotent navigation handoff; they never
|
|
78
|
+
prove a successful business commit. Do not combine this declaration with `readBack`.
|
|
79
|
+
Credential and privilege-state declarations are rejected. Never place actual
|
|
80
|
+
credentials in rules, fixtures, reports, or output.
|
|
81
|
+
|
|
82
|
+
Only an explicit click or key action on an `open`/`navigate` edge can perform
|
|
83
|
+
these handoffs. Edit/fill/validation/cancel, save/delete, HTTP mutations,
|
|
84
|
+
IndexedDB writes, `clear`, extra keys, duplicate writes, wrong values, and
|
|
85
|
+
unacknowledged success messages are not covered by this permission. A handoff
|
|
86
|
+
does not save an order or other business record.
|
|
87
|
+
|
|
88
|
+
The runner records storage operations across navigation and window closure,
|
|
89
|
+
captures bounded real before/after storage snapshots, checks the original
|
|
90
|
+
triggering/destination pages, and compares all other local/session keys.
|
|
91
|
+
Direct property assignments cannot hide an additional persistent key.
|
|
92
|
+
Snapshot observation is bounded to 256 keys and 1 MiB per page; unsupported
|
|
93
|
+
or oversized states fail explicitly. Reports contain failure identifiers,
|
|
94
|
+
not captured unexpected storage values.
|
|
95
|
+
|
|
96
|
+
## Failures remain failures
|
|
97
|
+
|
|
98
|
+
The exact browser identity, isolated preview origin, source-bound successful
|
|
99
|
+
tests, signed original plan/context, public field projection, original budget
|
|
100
|
+
and deadline, save/delete read-back, and unknown-attempt prohibition remain.
|
|
101
|
+
Preserve failed reports and screenshots. A stage or navigation declaration
|
|
102
|
+
cannot repair missing history, renew authority, approve a design, or report
|
|
103
|
+
the original business project complete.
|
package/IDE-EXECUTION.md
CHANGED
|
@@ -31,6 +31,23 @@ On first use or after a material agreement revision, the CLI returns a Confirm P
|
|
|
31
31
|
|
|
32
32
|
## Work with a task
|
|
33
33
|
|
|
34
|
+
### One task entrance approval
|
|
35
|
+
|
|
36
|
+
The normal task entry includes `task.authorization` alongside the original goal, all requirements and risk. Construct the concrete boundary from the user's actual task and repository inspection; do not add permissions for possible future work. The entrance Confirm Protocol question displays the boundary and risk together. Submit its original user's answer once. Route acceptance is an internal admission receipt, not another question. Existing genuine business choices may require an answer, but engine questions already answered by the task/context do not need to be asked again.
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{"schemaVersion":"entrorove.task-authorization/1.0","readPaths":["src","test","package.json"],"writePaths":["src/components/Product.vue"],"forbiddenPaths":["src/components/protected"],"actions":["modify","interface-design","visual-acceptance"],"dataOperations":[]}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Paths are canonical repository-relative prefixes. Write prefixes allow reads; forbidden prefixes always win. `actions` explicitly lists needed kinds: modify/create/delete/move/format, new-definition/contract-change/rewrite/delete-in-use, interface-design and visual-acceptance. Data actions additionally name their exact operation and target. The task's declared risk is its approved ceiling. This policy does not implicitly include deployment, other repositories, credentials or data migrations. Scope contracts, original requirements, budgets, signatures, actual snapshots and acceptance checks still apply.
|
|
43
|
+
|
|
44
|
+
Within this recorded boundary, design review, exact role review, read-only build-context correction, discipline review and delegated visual acceptance can return `approvalReused: true`, `humanPromptRequired: false` and `authorizationSource`. Continue their returned next operation directly. The persisted machine decision links the exact original entrance interaction, audit and policy digest; it is not a new human answer. Do not show it as another question or manufacture an `interaction-answer`. An uncovered path, action, higher risk or nondelegated business decision returns the actual additional question; explain only the concrete difference and consolidate necessary additions. A legacy task without an entrance boundary retains its real original approvals, rather than silently acquiring permissions.
|
|
45
|
+
|
|
46
|
+
Initialize the SDK/task/signer/snapshot once and reuse valid records. Public key pins load offline after authenticated enrollment. In one connected run, ordinary operations share endpoint/account-bound schema discovery. Fresh discovery is needed for a missing operation or explicit contract refresh. Completed native computations checkpoint independently, so recovery continues the unfinished skill instead of rerunning already completed skills. A historical scope proof is usable for planning only after validating its original identity, signature, consumed nonce and timely admission; it is not current execution authority. Recheck current sources, permissions and original M consumption for actual writes, then settle and test real effects.
|
|
47
|
+
|
|
48
|
+
Recovery has a durable bound: two automatic retries per unchanged verified prerequisite set or signing transition, three original GET recovery observations, and two retries of an explicitly known pre-admission prerequisite. Repeating the same reserved recovery after a crash does not allocate a new attempt. A known pre-effect request may continue after its original workflow records new verified execution, acceptance or approved stage progress. Preserve its original envelope, fenced generations, total attempts and append-only failures. Budgets for previous progress points remain spent; caller hints, a fresh signature, status polling, a new nonce, an empty journal or an altered task cannot reset them. `ENTROROVE_RECOVERY_EXHAUSTED` preserves the original result and pending evidence and disables retries without progress. Report the specific failure once for repair. Never replay unknown POST/PUT, clear records or repeatedly stop/ask the same permission.
|
|
49
|
+
|
|
50
|
+
|
|
34
51
|
### IDE display and cloud planning entry
|
|
35
52
|
|
|
36
53
|
For a new engineering task, run `npx --yes --prefer-online @entrorove/skill@latest invoke` with `operation: "start"` and the actual task facts below. The CLI reads the authenticated account's sandbox preference before dispatch. An enabled account returns `status: "needs-input"` and a cloud choice, with no model call yet. This is a routing question, not an execution failure. Render it immediately with the host's question tool or native UI; if unavailable, display `interaction.chatFallback` and collect the answer. A collapsed command log is insufficient. Use the returned locale-specific labels for **Use cloud sandbox** and **Continue locally**. After confirmation call the returned `cloud-task` callback with its exact original payload and `sandboxChoice: "use"`; exit repeats the original `start` with top-level `sandboxChoice: "local"`. Preserve the original goal, risk, work type and requirements. A disabled account continues through its configured local workflow; unavailable preferences stop entry explicitly. Reuse a prior explicit choice while resuming this task. Silence never authorizes cloud consumption.
|
|
@@ -88,7 +105,7 @@ For a returned `flowId`, continue with `{"operation":"continue","input":{"flowId
|
|
|
88
105
|
|
|
89
106
|
The v2 progress sequence is durable and ordered: the first runtime step reports `intake`, then the applicability `gate` is recorded and verified, then the selected contract or plan steps run. Every step can be `pending`, `running`, `reported`, `verified`, `blocked` or `failed`; only a completed verdict with trusted evidence is green.
|
|
90
107
|
|
|
91
|
-
When intake returns `workflow-gate`, connect the current repository before invoking that gate: run `npx @entrorove/skill scope-connect <absolute repositoryRoot>` with JSON `{"flowId":"<original flowId>","paths":["<existing source file>"]}` on stdin. Use the concrete existing source files in the task's scope; new files belong in the later plan's declared artifacts. The adapter enrolls an OS-private Ed25519 scope signer for this accepted account and task, answers a server challenge, copies and verifies real source files, and submits the signed facts. It sends hashes and scope metadata rather than source contents or the private key. Enrollment grants scope inspection only; execution evidence and write passes retain their separate authority checks. An operator-configured signer cannot be replaced. After a successful connection, use the returned `nextInput` for `continue`, or explicitly send `payload: {"targetFiles":["<same existing source file>"]}` to the original `workflow-gate`. The connector uses `paths`; the gate uses `targetFiles`.
|
|
108
|
+
When intake returns `workflow-gate`, connect the current repository before invoking that gate: run `npx @entrorove/skill scope-connect <absolute repositoryRoot>` with JSON `{"flowId":"<original flowId>","paths":["<existing source file>"]}` on stdin. Use the concrete existing source files in the task's scope; new files belong in the later plan's declared artifacts. The adapter enrolls an OS-private Ed25519 scope signer for this accepted account and task, answers a server challenge, copies and verifies real source files, and submits the signed facts. It sends hashes and scope metadata rather than source contents or the private key. Enrollment grants scope inspection only; execution evidence and write passes retain their separate authority checks. An operator-configured signer cannot be replaced. After a successful connection, use the returned `nextInput` for `continue`, or explicitly send `payload: {"targetFiles":["<same existing source file>"]}` to the original `workflow-gate`. The connector uses `paths`; the gate uses `targetFiles`. Planning reuses the timely admitted original proof; refresh only when a current execution or changed-source check requires it, using the same flow and path set. Show an explicit blocked result; query the original request ID after an unknown transport outcome before considering another attempt.
|
|
92
109
|
|
|
93
110
|
Scope inspection resolves aliases from actual `tsconfig.json` or `jsconfig.json`, including relative and npm `extends` and ordered inheritance arrays. Npm configuration lookup uses the pinned TypeScript JSON-config resolver over controlled metadata reads, including scoped packages, subpaths, package `tsconfig`, `exports` and `typesVersions`. Read-only candidate probes treat a child beneath an ordinary file as absent so normal TypeScript fallbacks continue; actual sources, control files and mutation targets retain strict validation. Rejected paths report the requested path, blocking ancestor, resolved target and reason, including broken or cyclic dependency links. Configurations and package metadata enter the signed hashes; dependencies are read only, never executed. Repository-local pnpm links use their actual configuration location. Read-only external dependency packages support parents and mappings inside their reference boundary; references requiring an unrepresentable external canonical ancestor fail explicitly rather than selecting a different same-name repository package. For plugin source compiled by a host project whose config is outside the source's ancestors, include `importContexts: [{"sourcePrefix":"<plugin source directory>","configPath":"<actual host tsconfig.json>"}]` in `scope-connect` input. Inspect the real build configuration first; never invent an alias or mark an unresolved import as external. These context paths remain inside the repository, their hashes enter the signed facts, and the local task binding freezes them.
|
|
94
111
|
|
|
@@ -102,7 +119,7 @@ An unresolved alias can occur after the original local signer has been enrolled
|
|
|
102
119
|
|
|
103
120
|
First inspect the actual host configurations and the real build source that copies or compiles the plugin mirror. Submit JSON on stdin with `action: "review"`, the original `flowId`, the complete `importContexts` list (all original mappings unchanged, followed by the concrete missing mappings), `evidencePaths` naming the actual repository build-source files, and a concrete `reason`. Each mapping still has exactly `sourcePrefix` and `configPath`. Prefixes must identify actual directories and configurations must be real repository `tsconfig` or `jsconfig` files. No generated alias or invented project configuration can substitute.
|
|
104
121
|
|
|
105
|
-
The SDK signs the exact proposal with the original S key. The service checks the original accepted owner, task, repository, signer, complete target set and current context revision, then issues an original-owner Confirm Protocol question showing the exact old/new mappings and observed evidence hashes.
|
|
122
|
+
The SDK signs the exact proposal with the original S key. The service checks the original accepted owner, task, repository, signer, complete target set and current context revision, then issues an original-owner Confirm Protocol question showing the exact old/new mappings and observed evidence hashes. If `approvalReused` is true, continue the authorized context correction without another question. Otherwise show the concrete uncovered context question using the host question tool, and submit only the actual user's answer to its original callback. Silence remains pending; a caller flag cannot provide approval. These configuration and build-evidence files remain read-only and hash-bound during later scope refreshes and file-role amendments; this approval cannot authorize changing their alias semantics or source bytes.
|
|
106
123
|
|
|
107
124
|
After approval, run `scope-context` with `{ "action": "apply", "flowId": "<original flow>", "proposalId": "<returned proposal>", "proposalDigest": "<returned digest>" }`. It prepares and independently inspects a real copied snapshot and import graph using all original target paths and the proposed contexts, then obtains a fresh server challenge before the original S signature. The short challenge lifetime remains unchanged. Its actual read set, source hashes, original missing resolver candidates, build evidence and snapshot are rechecked after that request; drift rejects signing. A local parser failure is archived, and retrying the identical approved input reuses its original snapshot only while the actual files still match. Unknown remote outcomes require the original GET recovery before any new challenge. Configuration or build-evidence drift, an unresolved actual alias, stale revision, expired/consumed nonce, foreign identity or missing approval rejects the attempt. No context approval grants writes or completes the task.
|
|
108
125
|
|
|
@@ -112,9 +129,9 @@ Successful application appends a server-signed context authority and local histo
|
|
|
112
129
|
|
|
113
130
|
If the original selection omitted actual components or inspection files, use the discovered `workflow-scope-review` on the **same flow**, with its original gate digest, current effective revision (`0` before the first amendment), and the complete sorted exact file map. Each entry declares its real `path`, `sha256`, `access` (`write` or `read-only`), original `requirementRefs` and reason. Include all original entries even when reducing their access to read-only. Existing dependencies may become writable only through this exact new review; a directory prefix or manifest inclusion does not grant that permission.
|
|
114
131
|
|
|
115
|
-
|
|
132
|
+
For `approvalReused: true`, continue the exact approved role revision directly. Otherwise present the concrete scope difference once and submit the original owner's actual answer through its bound `interaction-answer`. Approval is specific to that complete proposal; it does not approve a plan or perform a write. Then run `npx @entrorove/skill scope-amend <original repositoryRoot>` with `{flowId,proposalId,proposalDigest}` on stdin. It loads the **existing** S key and original repository/entry/import-context binding, obtains a W challenge, scans the real complete file set into a separate candidate snapshot and submits signed evidence. It never creates a replacement signer, fabricates a human answer, or overwrites the original scope history.
|
|
116
133
|
|
|
117
|
-
When exact new paths imply a higher risk, the service includes `riskReview` in the immutable proposal and explicitly shows the old and proposed risk in its original-owner question.
|
|
134
|
+
When exact new paths imply a higher risk, the service includes `riskReview` in the immutable proposal and explicitly shows the old and proposed risk in its original-owner question. An entrance authorization covering these exact paths and that risk ceiling can be reused; otherwise only the actual additional approval raises the exact scope risk ceiling; the original task, mode, path contract and consumed file/token/line/time budgets remain unchanged. An older approval lacking this risk question cannot authorize an upgrade: follow `risk-reassessment-required` with the same complete role map in a new `workflow-scope-review` on the original flow. Keep the old approval and failed request history; never lower observed risk or repeat an unchanged rejected apply.
|
|
118
135
|
|
|
119
136
|
Only a successful native gate and the original approved contracts and remaining budgets can activate the immutable scope revision. Keep the original Blueprint, plan identities, task order and failure receipts. Submit the complete corrected plan with the returned `scopeRevisionDigest` under its canonical new request identity. Exact read-only files remain read-only, even if included in the manifest or a broad write contract. An unknown request outcome is recovered with the original GET; do not replay an uncertain POST or restart the flow.
|
|
120
137
|
|
package/REUSE-DISCIPLINE.md
CHANGED
|
@@ -9,13 +9,13 @@ Snapshot scans stream whole-repository integrity hashes and bound semantic work
|
|
|
9
9
|
Directory inventory appends each file to one shared list instead of expanding a whole subtree into call arguments. Large ignored directories, including `.artifacts`, remain in both integrity inventories; only `.git` and `node_modules` are excluded. Symlinks remain entries without traversal, and inventory drift still rejects the observation.
|
|
10
10
|
|
|
11
11
|
- D-1: Before another business mapping or fact definition, search actual definitions and reuse them. Snapshot planning records the whole repository comparison and AST usage review in `discipline-review.json`; its digest is covered by the signed original snapshot. Typed source-entity field mappings use the existing import/alias resolver to identify the same entity, then compare field/value sets. A conflicting second mapping is refused with the existing file and line. An unresolved semantic relation remains unknown. No overlap threshold is invented.
|
|
12
|
-
- D-2: A rewrite must cite a concrete existing defect at file:line. “Clearer”, “more maintainable” and “more general” are not evidence. A necessary sensitive exception is submitted through `continue` → `workflow-discipline-review` on the original planning phase. Display the whole concrete proposal.
|
|
12
|
+
- D-2: A rewrite must cite a concrete existing defect at file:line. “Clearer”, “more maintainable” and “more general” are not evidence. A necessary sensitive exception is submitted through `continue` → `workflow-discipline-review` on the original planning phase. Display the whole concrete proposal. Authorization is the original account's task entrance answer and audit when this exact action is covered; otherwise obtain one concrete additional decision. A caller's `accepted:true` grants nothing.
|
|
13
13
|
- D-3: A parallel path requires frozen `policy.parallelPaths` and actual same-input, fieldwise equivalence evidence. Use the existing GoldenBaseline to pin baseline bytes and source hash. The approved runner reads the baseline and actual process output; the original output cannot be supplied anew by the implementation. Missing evidence yields incomplete, mismatch yields blocked with field names. Baselines come from the existing path's actual independent samples, not the new implementation's self-consistency tests.
|
|
14
14
|
- D-4: `discipline-review` exposes symbols, actual references, unresolved sources, scan scope and artifact digest. States are in-use, unused and unknown. Exported/external or dynamic references remain unknown, including reflective/computed access. Unknown equals in-use for preservation. A deletion must reference the original usage review, named symbols and task requirement; known in-use deletion additionally needs the explicit approved exception. Zero static references alone never prove business removability.
|
|
15
|
-
- D-5: The cloud-signed `disciplinePolicy.writes` declares modify/create/delete/move/format with reason and original requirement IDs. Deletes and moves need
|
|
15
|
+
- D-5: The cloud-signed `disciplinePolicy.writes` declares modify/create/delete/move/format with reason and original requirement IDs. Deletes and moves need explicit authorization in the task entrance boundary or a concrete additional approval when outside it. `guarded-delete` and `guarded-move` compare the exact original declaration and physical baseline before action. Moves record explicit source/destination/digest events. Formatting on unchanged semantic lines is refused. The rollback snapshot is limited to pass paths; `discipline-check` compares the whole repository, including ignored files and Finder metadata, excluding only `.git` and `node_modules`. Outside-scope bytes or modes refuse verification. Expired completed-write consumption has the narrowly evidenced environment-observation recovery in IDE-EXECUTION.md; it neither changes this comparison nor grants later writes. No self-score grants permission.
|
|
16
16
|
- D-6: Future abstractions require concrete named fields from current requirements. Speculative extension justifications without those fields are refused.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Check the original task authorization for an actual same-semantic new definition, existing data/interface semantic change, data deletion/migration/rebuild, or rewrite. Pause only for a permission outside that approved boundary or a failed safety/equivalence check. Submit the exact proposal and reasons through the original flow. Existing data actions must pin their actual pre-execution snapshot artifact in the frozen runner manifest. Never delete to “try” a diagnosis.
|
|
19
19
|
|
|
20
20
|
For an independent new file with no same-semantic definition, documentation/comments/copy, or explicit one-off exploratory work, avoid unnecessary review round trips. Keep the current declared scope and run an executable assertion. Unknown size or overlap thresholds cannot enable an exemption: AC-B1 deletion ratio, AC-B2 overlap, AC-B3 small-change size and AC-B4 scan size stay null pending measurement.
|
|
21
21
|
|
package/SKILL.md
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: entrorove
|
|
3
|
-
description:
|
|
3
|
+
description: Route a task selected for Entrorove among its eight skills, approve its concrete boundary once, and reuse that authorization and verified results at handoffs. Preserve scope, real execution evidence and functional plus visual acceptance. Hooks are optional.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# entrorove · 熵巡
|
|
7
7
|
|
|
8
|
-
Version: 0.1.
|
|
8
|
+
Version: 0.1.78.
|
|
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
|
|
|
12
|
+
Operate only on the repositories and sources explicitly selected for this task. Never embed a private business checkout, fixed unrelated chat identity or project-specific repair into the skill. Cross-project chat monitoring is outside this skill. Keep the eight skills and their necessary execution services independent of other projects.
|
|
13
|
+
|
|
14
|
+
## Task entrance and handoffs
|
|
15
|
+
|
|
16
|
+
Entrorove connects the eight existing skills. Select the skills that the actual task needs; a small direct task does not need a full eight-skill chain. At entrance, collect the goal, all requirements, concrete read/write prefixes, protected paths, necessary action kinds and risk together. Put that boundary in `task.authorization` using [the task entrance contract](IDE-EXECUTION.md#one-task-entrance-approval). Present and record one original-user approval. Within that boundary, follow `approvalReused: true` without another question or `interaction-answer`. Extra authorization is needed only for an actual permission or unresolved business decision outside the approved boundary; bundle the concrete differences. Never invent an approval for a legacy task lacking this record.
|
|
17
|
+
|
|
18
|
+
Reuse the task binding, installed public key pin, accepted snapshot identity, schemas and completed native skill results. A handoff checks identities and required differences; it does not reinstall the SDK, reapprove the same task or replay earlier successful skills. Planning verifies that stored scope evidence was timely admitted. Current execution still needs the original valid mutation authority, current source/snapshot checks and actual receipts; a historical proof cannot authorize a write. Prepare complete payloads before the short execution challenge and group already planned writes under their original pass.
|
|
19
|
+
|
|
20
|
+
Automatic recovery is finite: at most two retries with unchanged verified prerequisites, two retries of an unchanged signing transition, and three original GET recovery observations. A known pre-effect request can continue when its original workflow records new verified prerequisites; its original input, total attempts and failure history remain intact. A process restart, status poll, fresh signature or new nonce does not reset the saved counter. When exhausted without progress, preserve the original state and report the concrete failure once; do not generate further attempts, ask the same approval again, clear history or force Stop to loop. Unknown effects remain GET-only. See [IDE-EXECUTION.md](IDE-EXECUTION.md#one-task-entrance-approval).
|
|
21
|
+
|
|
12
22
|
## Required execution
|
|
13
23
|
|
|
14
24
|
1. Install `npx --yes --prefer-online @entrorove/skill@latest install --host codex` only when the user chooses to install this skill. The default installs the skill with hooks disabled and removes its recorded Codex hooks on upgrade, preserving other host settings. SDK calls work directly in an IDE without a hook callback or supported hook adapter; retain the actual host identity. Missing credentials, failed discovery and the SDK/API's scope, approval and evidence checks still return explicit failures.
|
|
@@ -16,11 +26,11 @@ Entrorove applies only to the task for which it is selected; reading these instr
|
|
|
16
26
|
`entrorove unlock` is the break-glass path: run the installed CLI directly in the affected IDE terminal. It writes a short-lived lease for that IDE session only, without a hook callback, network request, credential read, agreement check or protocol lookup. The current session ID must be available from the host environment or `ENTROROVE_SESSION`. The lease permits `flow-cancel` and `flow-abandon` recovery, never project writes or another session's calls. A successful `flow-abandon` clears that session's old binding locally even without a hook callback and preserves its queued requests.
|
|
17
27
|
To remove this skill and its host hooks, run `entrorove uninstall` directly. It works offline and preserves unrelated host settings, credentials and task records. If a managed file was edited, it reports the preserved path instead of deleting it.
|
|
18
28
|
2. Read [IDE-EXECUTION.md](IDE-EXECUTION.md) for the exact entry, cloud callbacks, account agreement and operation contracts. Use the stored account credential; never put it in arguments, URLs, project files, output or fixtures. Entrorove is independent of other systems.
|
|
19
|
-
3. Discover `capabilities`, then invoke `start` with the actual goal, task type, risk
|
|
29
|
+
3. Discover `capabilities`, then invoke `start` with the actual goal, requirements, task type, risk, scope facts and `task.authorization` boundary. An enabled account returns a cloud choice before charging a model. Include that route in the entrance decision; reuse an explicit choice already made by the user. Confirmation calls its original callback; exit adopts the local route for this task. Never treat silence as permission, invent acceptance, or create both routes. Existing explicit authorization for this task persists.
|
|
20
30
|
With cloud sandbox disabled, the IDE performs its own planning and continues the original API flow. Do not require a cloud model call or create a sandbox task to perform local work. The service freezes eligible member plans with `verificationMode: member-local` and `runnerKeyId: null`; successful original IDE execution and tests satisfy local acceptance. This mode comes from the stored account preference and enrolled scope, never a caller flag or an unavailable validator key. An already frozen independent plan keeps its original acceptance contract.
|
|
21
31
|
When the model produces a task contract, preserve its `workType`, `risk`, `difficulty`, `crossModule`, `parallel`, `unclear`, `architectureContract`, optional `baselineRef`, and optional `interaction` as returned. Valid work types are `implementation`, `documentation`, `calculation`, `merge`, `review`, `decision`, and `diagnosis`. A `merge` requires a concrete `baselineRef` identifying the branch, tag, commit, or review baseline; keep that baseline unchanged through independent verification. A `decision` requires `interaction`, including a risk description for medium or high risk. Do not fill missing required fields by guessing.
|
|
22
32
|
4. After real cloud acceptance, immediately display the returned entering status in the task’s selected language, including its **taskNumber**. Preserve `taskNumber` verbatim: it is the homepage sandbox tab number. Keep its UUID `taskId` separately. Run the returned `cloud-watch`, show each real public event and its cursor, and continue the original accepted flow from status. A command log alone is insufficient. No accepted cloud task means no cloud ID or model-execution claim.
|
|
23
|
-
5. For an accepted `flowId`, execute the returned `nextStep` with its required payload.
|
|
33
|
+
5. For an accepted `flowId`, execute the returned `nextStep` with its required payload. Use the bound discovered schema; ordinary steps reuse discovery instead of fetching it repeatedly. An empty payload is valid only when that operation's schema allows it. Reconnect and refresh only the original flow. A changed snapshot is a binding failure to investigate; do not invent a fresh successful baseline, rebuild a flow to evade it, or erase the old failure. If a fresh scope proof returns `ENTROROVE_SCOPE_PLANNING_REASSESSMENT_REQUIRED` before plan freeze, follow its explicit `workflow-scope-reassess` recovery and retry the original complete request; a local reassessment or snapshot refresh alone does not renew the accepted gate. If the terminal flow truly owns a stale task slot, use the explicit `flow-abandon` lifecycle operation described below before routing the original task again.
|
|
24
34
|
6. All governed mutations use the original authority: `local guarded-write`, `local guarded-delete`, `local guarded-move`, or the original frozen approved runner plan. Native patches, scripts, terminal writes, database mutations and unknown MCP operations must not substitute for the controlled entry. User-authorized task messages use the host's message tool and remain subject to host approval; sending a report does not change the flow or grant execution authority. **A failed gate or unknown outcome must never be replaced with another executor or reported as local success.** Query the original request read-only, repair the cause, and obtain the original flow's valid authority before proceeding. If the original scope challenge preparation is absent from authenticated status, `scope-amend` may use the discovered `scope-challenge-retire` lifecycle operation. The server validates the same approved proposal and receiver, atomically fences only an unadmitted challenge, and records a signed retirement without executing that challenge. Recover and acknowledge the original retirement result with GET before a fresh challenge attempt; retain both requests and all failures. An admitted request, issued nonce, scope application or mutation has no missing-challenge retirement path. An uncertain retirement itself permits only its original GET.
|
|
25
35
|
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).
|
|
26
36
|
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.
|
package/cli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { localSourceArguments, localSourceInstall, cliErrorText, startFsTrace } from './local-source-execution.mjs';
|
|
2
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
4
|
import { promises as fs, realpathSync } from 'node:fs';
|
|
4
5
|
import os from 'node:os';
|
|
@@ -26,6 +27,7 @@ import { exitIdeSession } from './ide-exit.mjs';
|
|
|
26
27
|
import { installOptions } from './ide-command.mjs';
|
|
27
28
|
import { readCliInput } from './cli-input.mjs';
|
|
28
29
|
const markerName = '.entrorove-managed.json';
|
|
30
|
+
let localSourceExecution = false, stopFsTrace = () => {};
|
|
29
31
|
const installFiles = ['SKILL.md', 'CAPABILITY-MAP.md', 'AGREEMENT.md', 'manifest.json', 'IDE-EXECUTION.md', 'REUSE-DISCIPLINE.md', 'DELIVERY-BOUNDARIES.md', 'DESIGN-GATES.md'];
|
|
30
32
|
const markerSchema = 'entrorove.managed-install/1.0';
|
|
31
33
|
const agreementSchema = 'entrorove.agreement-acceptance/1.0';
|
|
@@ -260,6 +262,7 @@ export async function install({ home = os.homedir(), cwd = process.cwd(), explic
|
|
|
260
262
|
const selected = ideAdapters.find(adapter => adapter.id === (hostId ?? 'codex'));
|
|
261
263
|
if (hostId !== undefined && hostId !== 'codex') throw new Error('Install host must be codex');
|
|
262
264
|
const target = globalInstallPath(home);
|
|
265
|
+
if (localSourceExecution) return localSourceInstall(target, await managedState(target), await sourceBundle(), { explicit, hostId, hooks });
|
|
263
266
|
if (!explicit && !(await managedState(target))) return { installed: false, updated: false };
|
|
264
267
|
const parent = path.dirname(target);
|
|
265
268
|
await fs.mkdir(parent, { recursive: true, mode: 0o700 });
|
|
@@ -297,7 +300,7 @@ export async function uninstall({ home = os.homedir() } = {}) {
|
|
|
297
300
|
|
|
298
301
|
async function stdinJson() {
|
|
299
302
|
const args = process.argv.slice(2);
|
|
300
|
-
const value = await readCliInput(process.stdin, args);
|
|
303
|
+
const value = await readCliInput(process.stdin, localSourceArguments(args).args);
|
|
301
304
|
if (!['configure','agreement','review'].includes(args[0])) await assertIdeExecution(args, value);
|
|
302
305
|
return value;
|
|
303
306
|
}
|
|
@@ -432,7 +435,8 @@ async function runAccountCommand(command, args) {
|
|
|
432
435
|
}
|
|
433
436
|
|
|
434
437
|
async function main() {
|
|
435
|
-
const
|
|
438
|
+
const selected = localSourceArguments(process.argv.slice(2)); localSourceExecution = selected.localSource; const [command, ...args] = selected.args;
|
|
439
|
+
if (localSourceExecution) { if (selected.traceFs) stopFsTrace = startFsTrace(); await install(); }
|
|
436
440
|
if (command === 'exit' && args.length === 0) {
|
|
437
441
|
process.stdout.write(`${JSON.stringify(await exitIdeSession())}\n`);
|
|
438
442
|
} else if (command === 'unlock' && args.length === 0) {
|
|
@@ -490,7 +494,7 @@ async function main() {
|
|
|
490
494
|
}
|
|
491
495
|
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) { const { captureIdeResults } = await import('./ide-results.mjs');
|
|
492
496
|
const finish = captureIdeResults();
|
|
493
|
-
main().catch((error) => { process.stderr.write(`${error
|
|
497
|
+
main().catch((error) => { process.stderr.write(`${cliErrorText(error)}\n`);
|
|
494
498
|
process.exitCode = 1;
|
|
495
|
-
}).finally(finish).catch(() => { process.stderr.write('Entrorove IDE completion could not be saved; recover the original request.\n'); process.exitCode = 1; });
|
|
499
|
+
}).finally(async () => { stopFsTrace(); await finish(); }).catch(() => { process.stderr.write('Entrorove IDE completion could not be saved; recover the original request.\n'); process.exitCode = 1; });
|
|
496
500
|
}
|
|
@@ -6,6 +6,7 @@ import { mutationStateRoot14, mutationFile14, mutationFail14, privateMutationDir
|
|
|
6
6
|
readMutationJson14, atomicMutationJson14 } from './local-mutation-files.mjs';
|
|
7
7
|
import { invokeRemoteResult } from './remote-client.mjs';
|
|
8
8
|
import { trustedOrigin } from './remote-transport.mjs';
|
|
9
|
+
import { prerequisiteFields } from './service-prerequisite.mjs';
|
|
9
10
|
|
|
10
11
|
const pinSchema = 'entrorove.cloud-mutation-authority-pin/1.0';
|
|
11
12
|
const keySchema = 'entrorove.mutation-authority-key/1.0';
|
|
@@ -71,13 +72,18 @@ export async function hydrateCloudMutationAuthority(home, apiOrigin) {
|
|
|
71
72
|
process.env[keyEnvironment] = pin.authority.publicKey;
|
|
72
73
|
return true;
|
|
73
74
|
}
|
|
74
|
-
export async function bootstrapCloudMutationAuthority(dependencies) {
|
|
75
|
+
export async function bootstrapCloudMutationAuthority(dependencies, { refresh = false } = {}) {
|
|
75
76
|
const origin = authorityOrigin(dependencies.apiOrigin()), location = await pinLocation(dependencies.home);
|
|
76
77
|
const previous = await readPin(location, origin);
|
|
77
78
|
if (process.env[keyEnvironment] !== undefined) publicIdentity(process.env[keyEnvironment]);
|
|
79
|
+
if (previous && !refresh) {
|
|
80
|
+
process.env[keyEnvironment] = previous.authority.publicKey;
|
|
81
|
+
return { keyId: previous.authority.keyId, apiOrigin: origin };
|
|
82
|
+
}
|
|
78
83
|
let result, response;
|
|
79
84
|
try { ({ result, response } = await invokeRemoteResult({ operation: 'capabilities', input: {} }, dependencies)); }
|
|
80
|
-
catch {
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (prerequisiteFields(error).servicePrerequisite) throw error;
|
|
81
87
|
throw Object.assign(new Error('Authenticated cloud mutation authority discovery is unavailable; no mutation operation was transmitted'),
|
|
82
88
|
{ code: 'ENTROROVE_MUTATION_AUTHORITY_DISCOVERY', knownBeforeTransmission: true });
|
|
83
89
|
}
|
package/cloud-task-client.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prerequisiteError, prerequisiteFields } from './service-prerequisite.mjs';
|
|
1
2
|
import { createHash } from 'node:crypto';
|
|
2
3
|
import { appendInvocation } from './local-invocations.mjs';
|
|
3
4
|
import { parseTransportResponse, requestTransport, trustedOrigin } from './remote-transport.mjs';
|
|
@@ -144,6 +145,12 @@ export async function runCloudTask(input, dependencies) {
|
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
if (!response.ok) {
|
|
148
|
+
const prerequisite = prerequisiteError(response, result, { requestId: id });
|
|
149
|
+
if (prerequisite) {
|
|
150
|
+
await appendInvocation({ ...metadata, event: 'rejected', servicePrerequisite: prerequisite.servicePrerequisite }, dependencies.home);
|
|
151
|
+
return envelope(task, 'blocked', { code: prerequisite.code, message: prerequisite.message,
|
|
152
|
+
...prerequisiteFields(prerequisite), requestId: id, httpStatus: response.status });
|
|
153
|
+
}
|
|
147
154
|
await appendInvocation({ ...metadata, event: response.status >= 500 ? 'outcome-unknown' : 'rejected' }, dependencies.home);
|
|
148
155
|
const code = typeof result?.error === 'string' ? result.error : 'cloud-task-rejected';
|
|
149
156
|
const rawMessage = typeof result?.message === 'string' ? result.message : code;
|
|
@@ -197,6 +204,9 @@ export async function queryCloudTask(taskId, dependencies) {
|
|
|
197
204
|
const taskResponse = await requestTransport(origin, `/api/cloud-sandbox/tasks/${id}`, { headers: authenticatedHeaders(token) });
|
|
198
205
|
let taskBody;
|
|
199
206
|
try { taskBody = await responseBody(taskResponse); } catch { taskBody = null; }
|
|
207
|
+
const prerequisite = prerequisiteError(taskResponse, taskBody);
|
|
208
|
+
if (prerequisite) return { schemaVersion: CLOUD_STATUS_SCHEMA, status: 'blocked', taskId: id,
|
|
209
|
+
code: prerequisite.code, message: prerequisite.message, ...prerequisiteFields(prerequisite) };
|
|
200
210
|
if (!taskResponse.ok || !taskBody?.task || taskBody.task.id !== id) {
|
|
201
211
|
await appendInvocation({ ...metadata, event: 'rejected' }, dependencies.home);
|
|
202
212
|
return { schemaVersion: CLOUD_STATUS_SCHEMA, status: taskResponse.status === 403 ? 'blocked' : taskResponse.status === 404 ? 'missing' : 'failed', taskId: id,
|
package/cloud-watch-client.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { queryCloudTask } from './cloud-task-client.mjs';
|
|
|
5
5
|
import { acknowledgeAutomaticFeedback } from './automatic-feedback-client.mjs';
|
|
6
6
|
import { sandboxTaskNumber } from './task-identity.mjs';
|
|
7
7
|
import { cloudMessage, cloudOutputLocale } from './cloud-locale.mjs';
|
|
8
|
+
import { fetchPrerequisiteError, streamPrerequisiteError } from './service-prerequisite.mjs';
|
|
8
9
|
|
|
9
10
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
10
11
|
function inputOptions(input) {
|
|
@@ -30,10 +31,15 @@ export async function watchCloudTask(input, dependencies) {
|
|
|
30
31
|
const response = await fetch(`${trustedOrigin(dependencies.apiOrigin())}/api/cloud-sandbox/tasks/${taskId}/events`, {
|
|
31
32
|
redirect: 'error',signal: controller.signal,headers: { authorization: `Bearer ${token}`,
|
|
32
33
|
accept: 'text/event-stream','last-event-id': afterId,...ideClientHeaders() } });
|
|
33
|
-
if (!response.ok)
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const prerequisite = await fetchPrerequisiteError(response, { taskId });
|
|
36
|
+
if (prerequisite) throw prerequisite;
|
|
37
|
+
throw new Error(`Cloud event stream unavailable: HTTP ${response.status}; taskId=${taskId}`);
|
|
38
|
+
}
|
|
34
39
|
if (!response.headers.get('content-type')?.startsWith('text/event-stream')) throw new Error('Cloud event response is not an event stream');
|
|
35
40
|
await consumeCloudStream(response.body,frame => {
|
|
36
|
-
if (frame.event === 'stream-error') throw
|
|
41
|
+
if (frame.event === 'stream-error') throw streamPrerequisiteError(frame.data, { taskId })
|
|
42
|
+
?? new Error(`Cloud event stream stopped: ${frame.data?.code ?? 'stream-failed'}`);
|
|
37
43
|
if (frame.event === 'status') {
|
|
38
44
|
if (frame.data?.taskId !== taskId || !['running','completed','blocked','failed','cancelled'].includes(frame.data.status)) throw new Error('Invalid cloud task status event');
|
|
39
45
|
taskStatus = frame.data.status; return taskStatus === 'running';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { designFail } from './design-policy.mjs';
|
|
2
|
+
import { checkNonCommitStorage } from './design-browser-state.mjs';
|
|
2
3
|
|
|
3
4
|
export async function browserAssertion(page, check) {
|
|
4
5
|
const locator = page.locator(check.selector);
|
|
@@ -33,20 +34,25 @@ export function networkCheck(actual, expected, failures) {
|
|
|
33
34
|
if (actual.length !== expected.reduce((sum, item) => sum + item.count, 0)
|
|
34
35
|
|| expected.some(item => actual.filter(event => event.method === item.method && event.path === item.path && event.status === item.status).length !== item.count)) failures.push('unexpected-http-mutation');
|
|
35
36
|
}
|
|
36
|
-
export async function installSuccessMonitor(
|
|
37
|
-
await
|
|
37
|
+
export async function installSuccessMonitor(target, successSelector, recordBinding, handoffKeys = []) {
|
|
38
|
+
await target.addInitScript(({ selector, binding, keys }) => {
|
|
38
39
|
const events = [], acknowledgements = [], storage = [], original = window.fetch.bind(window);
|
|
40
|
+
const record = event => { if (binding) window[binding](event).catch(() => {}); };
|
|
39
41
|
Object.defineProperty(window, '__entroroveDesignMonitor', { value: { events, acknowledgements, storage } });
|
|
40
42
|
for (const method of ['setItem', 'removeItem', 'clear']) {
|
|
41
43
|
const apply = Storage.prototype[method];
|
|
42
44
|
Storage.prototype[method] = function(...args) {
|
|
45
|
+
const area = this === localStorage ? 'local-storage' : this === sessionStorage ? 'session-storage' : 'unknown';
|
|
46
|
+
const key = typeof args[0] === 'string' ? args[0] : null, declared = keys.some(item => item.area === area && item.key === key);
|
|
47
|
+
const before = declared && method !== 'clear' ? this.getItem(key) : undefined;
|
|
43
48
|
const result = apply.apply(this, args); storage.push({ method, at: performance.now() });
|
|
49
|
+
record({ kind: 'storage', area, key, method, ...(declared ? { before, after: this.getItem(key) } : {}) });
|
|
44
50
|
acknowledgements.push({ at: performance.now(), ok: true }); return result;
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
53
|
if (typeof IDBObjectStore !== 'undefined') for (const method of ['add', 'put', 'delete', 'clear']) {
|
|
48
54
|
const apply = IDBObjectStore.prototype[method];
|
|
49
|
-
IDBObjectStore.prototype[method] = function(...args) { storage.push({ method: 'indexed-db-write', at: performance.now() }); return apply.apply(this, args); };
|
|
55
|
+
IDBObjectStore.prototype[method] = function(...args) { storage.push({ method: 'indexed-db-write', at: performance.now() }); record({ kind: 'storage' }); return apply.apply(this, args); };
|
|
50
56
|
}
|
|
51
57
|
window.fetch = async (...args) => {
|
|
52
58
|
const response = await original(...args);
|
|
@@ -66,13 +72,20 @@ export async function installSuccessMonitor(page, successSelector) {
|
|
|
66
72
|
const observe = () => {
|
|
67
73
|
const node = document.querySelector(selector), style = node && getComputedStyle(node);
|
|
68
74
|
const next = Boolean(node && style.display !== 'none' && style.visibility !== 'hidden' && node.getClientRects().length);
|
|
69
|
-
if (next && !visible)
|
|
75
|
+
if (next && !visible) {
|
|
76
|
+
const event = { at: performance.now(), successfulAcknowledgement: acknowledgements.some(item => item.ok) };
|
|
77
|
+
events.push(event); record({ kind: 'success', ...event });
|
|
78
|
+
}
|
|
70
79
|
visible = next;
|
|
71
80
|
};
|
|
72
81
|
document.addEventListener('DOMContentLoaded', () => {
|
|
73
82
|
new MutationObserver(observe).observe(document.documentElement, { attributes: true, childList: true, subtree: true, characterData: true }); observe();
|
|
74
83
|
});
|
|
75
|
-
}, successSelector);
|
|
84
|
+
}, { selector: successSelector, binding: recordBinding, keys: handoffKeys });
|
|
85
|
+
}
|
|
86
|
+
export async function checkBrowserMonitors(page, commits, canSucceed, failures) {
|
|
87
|
+
await checkNonCommitStorage(page, commits, failures);
|
|
88
|
+
await monitorSuccess(page, failures, canSucceed);
|
|
76
89
|
}
|
|
77
90
|
export async function monitorSuccess(page, failures, canSucceed) {
|
|
78
91
|
const observed = await page.evaluate(() => window.__entroroveDesignMonitor?.events ?? []);
|