mobile-debug-mcp 0.30.0 → 0.30.2
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/dist/interact/index.js +876 -155
- package/dist/observe/index.js +1 -0
- package/dist/observe/snapshot-metadata.js +62 -1
- package/dist/server/tool-definitions.js +20 -11
- package/dist/server/tool-handlers.js +7 -5
- package/dist/server-core.js +1 -1
- package/docs/CHANGELOG.md +9 -0
- package/docs/ROADMAP.md +85 -209
- package/docs/{rfcs → specs-deltas}/013-wait-and-synchronization-reliability.md +15 -35
- package/docs/{rfcs → specs-deltas}/014-actionability-resolution.md +32 -30
- package/docs/specs-deltas/015-adjustable-control-support.md +541 -0
- package/package.json +1 -1
- package/skills/spec-review/SKILL.md +52 -0
- package/skills/spec-review/references/spec-review-checklist.md +13 -0
- package/skills/spec-review/references/spec-review-template.md +28 -0
- package/src/interact/index.ts +1008 -171
- package/src/observe/index.ts +1 -0
- package/src/observe/snapshot-metadata.ts +69 -2
- package/src/server/tool-definitions.ts +20 -11
- package/src/server/tool-handlers.ts +7 -5
- package/src/server-core.ts +1 -1
- package/src/types.ts +26 -0
- package/test/unit/interact/adjust_control.test.ts +690 -33
- package/test/unit/interact/subtree_collection.test.ts +24 -0
- package/test/unit/interact/tap_element.test.ts +71 -0
- package/test/unit/interact/wait_for_ui_change.test.ts +92 -1
- package/test/unit/observe/snapshot_metadata.test.ts +67 -0
- package/test/unit/server/contract.test.ts +4 -0
- package/skills/rfc-review/SKILL.md +0 -52
- package/skills/rfc-review/references/rfc-review-checklist.md +0 -12
- package/skills/rfc-review/references/rfc-review-template.md +0 -28
- /package/docs/{rfcs → specs-deltas}/001-state-verification.md +0 -0
- /package/docs/{rfcs → specs-deltas}/002-richer-element-identity +0 -0
- /package/docs/{rfcs → specs-deltas}/003-wait-and-synchronization-reliability.md +0 -0
- /package/docs/{rfcs → specs-deltas}/004-action-verification-routing.md +0 -0
- /package/docs/{rfcs → specs-deltas}/005-unified-action-execution-and-verification-model.md +0 -0
- /package/docs/{rfcs → specs-deltas}/006-runtime-action-instrumentation-and-binding-layer.md +0 -0
- /package/docs/{rfcs → specs-deltas}/007-actionability-resolution-and-executable-target-selection.md +0 -0
- /package/docs/{rfcs → specs-deltas}/008-adjustable-control-support-and-semantic-value-manipulation.md +0 -0
- /package/docs/{rfcs → specs-deltas}/009-semantic-control-modeling-for-custom-and-composite-controls.md +0 -0
- /package/docs/{rfcs → specs-deltas}/010-verification-stabilization-and-temporal-convergence.md +0 -0
- /package/docs/{rfcs → specs-deltas}/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows.md +0 -0
- /package/docs/{rfcs → specs-deltas}/011.1-recovery-contract-types-and-runtime-wiring-spec.md +0 -0
- /package/docs/{rfcs → specs-deltas}/012-action-trace-and-xecution-observability.md +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { ToolsInteract } from '../../../src/interact/index.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
console.log('Starting subtree_collection unit tests...')
|
|
6
|
+
|
|
7
|
+
const elements = [
|
|
8
|
+
{ text: 'Root', resourceId: 'root-node', stable_id: 'root-stable', bounds: [0, 0, 100, 100], visible: true, enabled: true, clickable: true },
|
|
9
|
+
{ text: 'Numeric child', parentId: '0', stable_id: 'numeric-child', bounds: [0, 0, 50, 50], visible: true, enabled: true, clickable: true },
|
|
10
|
+
{ text: 'Resource child', parentId: 'root-node', stable_id: 'resource-child', bounds: [0, 50, 50, 100], visible: true, enabled: true, clickable: true },
|
|
11
|
+
{ text: 'Stable child', parentId: 'root-stable', stable_id: 'stable-child', bounds: [50, 0, 100, 50], visible: true, enabled: true, clickable: true },
|
|
12
|
+
{ text: 'Grandchild', parentId: 'resource-child', bounds: [50, 50, 100, 100], visible: true, enabled: true, clickable: true }
|
|
13
|
+
] as any[]
|
|
14
|
+
|
|
15
|
+
const indices = (ToolsInteract as any)._collectSubtreeIndices(elements, 0)
|
|
16
|
+
assert.deepStrictEqual(indices, [0, 1, 2, 3, 4])
|
|
17
|
+
|
|
18
|
+
console.log('subtree_collection unit tests passed')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
run().catch((error) => {
|
|
22
|
+
console.error(error)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
})
|
|
@@ -84,6 +84,77 @@ async function run() {
|
|
|
84
84
|
assert.strictEqual(disabledResult.success, false)
|
|
85
85
|
assert.strictEqual(disabledResult.failure_code, 'ELEMENT_NOT_INTERACTABLE')
|
|
86
86
|
|
|
87
|
+
;(Observe as any).ToolsObserve.getUITreeHandler = async () => ({
|
|
88
|
+
device: { platform: 'android', id: 'mock-device' },
|
|
89
|
+
elements: [
|
|
90
|
+
{ text: 'Plain label', resourceId: 'txt_plain', bounds: [0, 0, 20, 20], visible: true, enabled: true, clickable: false }
|
|
91
|
+
]
|
|
92
|
+
})
|
|
93
|
+
const waitPlain = await ToolsInteract.waitForUIHandler({
|
|
94
|
+
selector: { text: 'Plain label' },
|
|
95
|
+
condition: 'exists',
|
|
96
|
+
timeout_ms: 200,
|
|
97
|
+
poll_interval_ms: 50,
|
|
98
|
+
platform: 'android'
|
|
99
|
+
})
|
|
100
|
+
const plainResult = await ToolsInteract.tapElementHandler({ elementId: waitPlain.element.elementId })
|
|
101
|
+
assert.strictEqual(plainResult.success, false)
|
|
102
|
+
assert.strictEqual(plainResult.failure_code, 'ELEMENT_NOT_INTERACTABLE')
|
|
103
|
+
|
|
104
|
+
;(Observe as any).ToolsObserve.getUITreeHandler = async () => ({
|
|
105
|
+
device: { platform: 'ios', id: 'ios-mock' },
|
|
106
|
+
elements: [
|
|
107
|
+
{
|
|
108
|
+
text: 'Semantic tap',
|
|
109
|
+
resourceId: 'ios_semantic_tap',
|
|
110
|
+
bounds: [10, 10, 30, 30],
|
|
111
|
+
visible: true,
|
|
112
|
+
enabled: true,
|
|
113
|
+
clickable: false,
|
|
114
|
+
semantic: {
|
|
115
|
+
is_clickable: true,
|
|
116
|
+
is_container: false,
|
|
117
|
+
supported_actions: ['tap']
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
})
|
|
122
|
+
const iosSemanticWait = await ToolsInteract.waitForUIHandler({
|
|
123
|
+
selector: { text: 'Semantic tap' },
|
|
124
|
+
condition: 'exists',
|
|
125
|
+
timeout_ms: 200,
|
|
126
|
+
poll_interval_ms: 50,
|
|
127
|
+
platform: 'ios'
|
|
128
|
+
})
|
|
129
|
+
const iosSemanticTap = await ToolsInteract.tapElementHandler({ elementId: iosSemanticWait.element.elementId })
|
|
130
|
+
assert.strictEqual(iosSemanticTap.success, true)
|
|
131
|
+
assert.strictEqual(iosSemanticTap.target.resolved?.elementId, iosSemanticWait.element.elementId)
|
|
132
|
+
assert.deepStrictEqual(tapped, { platform: 'ios', x: 20, y: 20, deviceId: 'ios-mock' })
|
|
133
|
+
|
|
134
|
+
;(Observe as any).ToolsObserve.getUITreeHandler = async () => ({
|
|
135
|
+
device: { platform: 'android', id: 'mock-device' },
|
|
136
|
+
elements: [
|
|
137
|
+
{ text: 'Stable target', resourceId: 'btn_stable', bounds: [0, 0, 20, 20], visible: true, enabled: true, clickable: true, stable_id: 'stable-1' }
|
|
138
|
+
]
|
|
139
|
+
})
|
|
140
|
+
const waitStable = await ToolsInteract.waitForUIHandler({
|
|
141
|
+
selector: { text: 'Stable target' },
|
|
142
|
+
condition: 'exists',
|
|
143
|
+
timeout_ms: 200,
|
|
144
|
+
poll_interval_ms: 50,
|
|
145
|
+
platform: 'android'
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
;(Observe as any).ToolsObserve.getUITreeHandler = async () => ({
|
|
149
|
+
device: { platform: 'android', id: 'mock-device' },
|
|
150
|
+
elements: [
|
|
151
|
+
{ text: 'Stable target', resourceId: 'btn_stable', bounds: [0, 0, 20, 20], visible: true, enabled: true, clickable: true, stable_id: 'stable-2' }
|
|
152
|
+
]
|
|
153
|
+
})
|
|
154
|
+
const stableMismatchResult = await ToolsInteract.tapElementHandler({ elementId: waitStable.element.elementId })
|
|
155
|
+
assert.strictEqual(stableMismatchResult.success, false)
|
|
156
|
+
assert.strictEqual(stableMismatchResult.failure_code, 'STALE_REFERENCE')
|
|
157
|
+
|
|
87
158
|
;(Observe as any).ToolsObserve.getUITreeHandler = async () => ({
|
|
88
159
|
device: { platform: 'android', id: 'mock-device' },
|
|
89
160
|
elements: []
|
|
@@ -6,7 +6,18 @@ type UiTree = {
|
|
|
6
6
|
device: { platform: 'android', id: string, osVersion: string, model: string, simulator: boolean }
|
|
7
7
|
screen: string
|
|
8
8
|
resolution: { width: number, height: number }
|
|
9
|
-
elements: Array<{
|
|
9
|
+
elements: Array<{
|
|
10
|
+
text: string
|
|
11
|
+
type: string
|
|
12
|
+
bounds: number[]
|
|
13
|
+
visible: boolean
|
|
14
|
+
enabled?: boolean
|
|
15
|
+
clickable?: boolean
|
|
16
|
+
stable_id?: string
|
|
17
|
+
parentId?: number
|
|
18
|
+
children?: number[]
|
|
19
|
+
state?: Record<string, unknown> | null
|
|
20
|
+
}>
|
|
10
21
|
snapshot_revision: number
|
|
11
22
|
captured_at_ms: number
|
|
12
23
|
}
|
|
@@ -22,6 +33,21 @@ function makeTree(screen: string, revision: number): UiTree {
|
|
|
22
33
|
}
|
|
23
34
|
}
|
|
24
35
|
|
|
36
|
+
function makeScopedTree(title: string, status: string, revision: number): UiTree {
|
|
37
|
+
return {
|
|
38
|
+
device: { platform: 'android', id: 'mock', osVersion: '14', model: 'Pixel', simulator: true },
|
|
39
|
+
screen: 'Scoped',
|
|
40
|
+
resolution: { width: 1080, height: 2400 },
|
|
41
|
+
elements: [
|
|
42
|
+
{ text: 'Root', type: 'FrameLayout', bounds: [0, 0, 1080, 2400], visible: true, enabled: true, stable_id: 'root', children: [1, 2] },
|
|
43
|
+
{ text: title, type: 'TextView', bounds: [0, 0, 1080, 200], visible: true, enabled: true, stable_id: 'title', parentId: 0, state: { text_value: title } },
|
|
44
|
+
{ text: status, type: 'TextView', bounds: [0, 200, 1080, 260], visible: true, enabled: true, stable_id: 'status', parentId: 0, state: { text_value: status } }
|
|
45
|
+
],
|
|
46
|
+
snapshot_revision: revision,
|
|
47
|
+
captured_at_ms: 1000 + revision
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
25
51
|
async function runScenario({
|
|
26
52
|
snapshots,
|
|
27
53
|
expectedChange,
|
|
@@ -72,6 +98,62 @@ async function runScenario({
|
|
|
72
98
|
}
|
|
73
99
|
}
|
|
74
100
|
|
|
101
|
+
async function runScopedScenario() {
|
|
102
|
+
const snapshots = [
|
|
103
|
+
makeScopedTree('Title', 'Status 1', 1),
|
|
104
|
+
makeScopedTree('Title', 'Status 2', 2),
|
|
105
|
+
makeScopedTree('Title Ready', 'Status 2', 3),
|
|
106
|
+
makeScopedTree('Title Ready', 'Status 2', 4)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
const originalGetUITreeHandler = (ToolsObserve as any).getUITreeHandler
|
|
110
|
+
const originalSetTimeout = globalThis.setTimeout
|
|
111
|
+
const originalDateNow = Date.now
|
|
112
|
+
let now = 0
|
|
113
|
+
let calls = 0
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
;(Date as any).now = () => now
|
|
117
|
+
;(globalThis as any).setTimeout = (callback: (...args: any[]) => void) => {
|
|
118
|
+
now += 1
|
|
119
|
+
callback()
|
|
120
|
+
return 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
;(ToolsObserve as any).getUITreeHandler = async () => snapshots[Math.min(calls++, snapshots.length - 1)]
|
|
124
|
+
|
|
125
|
+
const resolution = await ToolsInteract.waitForUIHandler({
|
|
126
|
+
selector: { text: 'Title' },
|
|
127
|
+
condition: 'exists',
|
|
128
|
+
timeout_ms: 1000,
|
|
129
|
+
poll_interval_ms: 1,
|
|
130
|
+
platform: 'android',
|
|
131
|
+
deviceId: 'mock'
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const targetId = resolution?.element?.elementId
|
|
135
|
+
assert.ok(targetId)
|
|
136
|
+
|
|
137
|
+
calls = 0
|
|
138
|
+
now = 0
|
|
139
|
+
const result = await ToolsInteract.waitForUIChangeHandler({
|
|
140
|
+
platform: 'android',
|
|
141
|
+
deviceId: 'mock',
|
|
142
|
+
expected_change: 'text_change',
|
|
143
|
+
scope: 'subtree',
|
|
144
|
+
target: targetId,
|
|
145
|
+
timeout_ms: 2000,
|
|
146
|
+
stability_window_ms: 1
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
return { result, targetId, calls }
|
|
150
|
+
} finally {
|
|
151
|
+
;(ToolsObserve as any).getUITreeHandler = originalGetUITreeHandler
|
|
152
|
+
;(globalThis as any).setTimeout = originalSetTimeout
|
|
153
|
+
;(Date as any).now = originalDateNow
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
75
157
|
async function run() {
|
|
76
158
|
const success = await runScenario({
|
|
77
159
|
snapshots: [makeTree('Loading', 1), makeTree('Loaded', 2)],
|
|
@@ -109,6 +191,15 @@ async function run() {
|
|
|
109
191
|
assert.strictEqual(defaultWindow.calls, 4)
|
|
110
192
|
assert.deepStrictEqual(defaultWindow.delays, [300, 300, 300])
|
|
111
193
|
|
|
194
|
+
const scoped = await runScopedScenario()
|
|
195
|
+
assert.strictEqual(scoped.result.success, true)
|
|
196
|
+
assert.strictEqual(scoped.result.scope, 'subtree')
|
|
197
|
+
assert.strictEqual(scoped.result.target, scoped.targetId)
|
|
198
|
+
assert.strictEqual(scoped.result.observed_change, 'text_change')
|
|
199
|
+
assert.strictEqual(scoped.result.change_summary?.added_elements, 0)
|
|
200
|
+
assert.strictEqual(scoped.result.change_summary?.removed_elements, 0)
|
|
201
|
+
assert.strictEqual(scoped.result.stability_state, 'stable')
|
|
202
|
+
|
|
112
203
|
const resetWindow = await runScenario({
|
|
113
204
|
snapshots: [makeTree('Loading', 1), makeTree('Loaded', 2), makeTree('Loaded-again', 3), makeTree('Loaded-again', 4), makeTree('Loaded-again', 5)],
|
|
114
205
|
expectedChange: 'text_change',
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { deriveSnapshotMetadata, resetSnapshotMetadataForTests } from '../../../src/observe/snapshot-metadata.js'
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
console.log('Starting snapshot_metadata unit tests...')
|
|
6
|
+
|
|
7
|
+
resetSnapshotMetadataForTests()
|
|
8
|
+
|
|
9
|
+
const deviceKey = 'android:mock'
|
|
10
|
+
const first = deriveSnapshotMetadata(deviceKey, {
|
|
11
|
+
screen: 'Home',
|
|
12
|
+
resolution: { width: 100, height: 200 },
|
|
13
|
+
elements: [
|
|
14
|
+
{
|
|
15
|
+
text: 'Alpha',
|
|
16
|
+
contentDescription: null,
|
|
17
|
+
resourceId: 'row_1',
|
|
18
|
+
type: 'TextView',
|
|
19
|
+
clickable: false,
|
|
20
|
+
enabled: true,
|
|
21
|
+
visible: true,
|
|
22
|
+
bounds: [0, 0, 10, 10],
|
|
23
|
+
state: null,
|
|
24
|
+
stable_id: 'stable-row'
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}, 'ui_tree')
|
|
28
|
+
|
|
29
|
+
assert.strictEqual(first.snapshot_revision, 1)
|
|
30
|
+
assert.strictEqual(first.snapshot_delta, null)
|
|
31
|
+
|
|
32
|
+
const second = deriveSnapshotMetadata(deviceKey, {
|
|
33
|
+
screen: 'Home',
|
|
34
|
+
resolution: { width: 100, height: 200 },
|
|
35
|
+
elements: [
|
|
36
|
+
{
|
|
37
|
+
text: 'Beta',
|
|
38
|
+
contentDescription: null,
|
|
39
|
+
resourceId: 'row_1',
|
|
40
|
+
type: 'TextView',
|
|
41
|
+
clickable: false,
|
|
42
|
+
enabled: true,
|
|
43
|
+
visible: true,
|
|
44
|
+
bounds: [0, 0, 10, 10],
|
|
45
|
+
state: null,
|
|
46
|
+
stable_id: 'stable-row'
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}, 'ui_tree')
|
|
50
|
+
|
|
51
|
+
assert.strictEqual(second.snapshot_revision, 2)
|
|
52
|
+
assert.deepStrictEqual(second.snapshot_delta, {
|
|
53
|
+
previous_snapshot_revision: 1,
|
|
54
|
+
added_elements: 0,
|
|
55
|
+
removed_elements: 0,
|
|
56
|
+
mutated_elements: 1,
|
|
57
|
+
total_elements: 1
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
resetSnapshotMetadataForTests()
|
|
61
|
+
console.log('snapshot_metadata unit tests passed')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
run().catch((error) => {
|
|
65
|
+
console.error(error)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
})
|
|
@@ -70,6 +70,10 @@ async function run() {
|
|
|
70
70
|
const adjustControl = toolDefinitions.find((tool) => tool.name === 'adjust_control')
|
|
71
71
|
assert(adjustControl, 'adjust_control should be registered')
|
|
72
72
|
assert.deepStrictEqual((adjustControl as any).inputSchema.required, ['targetValue'])
|
|
73
|
+
assert.deepStrictEqual((adjustControl as any).inputSchema.oneOf, [
|
|
74
|
+
{ required: ['selector'] },
|
|
75
|
+
{ required: ['element_id'] }
|
|
76
|
+
])
|
|
73
77
|
assert.strictEqual((adjustControl as any).inputSchema.properties.targetValue.type, 'number')
|
|
74
78
|
assert.match((adjustControl as any).description, /numeric control value/i)
|
|
75
79
|
assert.match((adjustControl as any).description, /expect_state/i)
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# RFC Review skill
|
|
2
|
-
|
|
3
|
-
name: rfc-review
|
|
4
|
-
version: 0.1.1
|
|
5
|
-
summary: Reusable workflow for reviewing RFCs/specs in this repository with a consistent readiness rubric and output template.
|
|
6
|
-
|
|
7
|
-
# Purpose
|
|
8
|
-
Help an agent review an RFC for clarity, implementation readiness, and alignment with the current codebase. Use a common template so reviews stay consistent across documents and reviewers.
|
|
9
|
-
|
|
10
|
-
# Activation conditions
|
|
11
|
-
Activate when an agent needs to:
|
|
12
|
-
- review a new or revised RFC
|
|
13
|
-
- assess whether an RFC is implementation-ready
|
|
14
|
-
- identify whether feedback is an RFC issue or an implementation issue
|
|
15
|
-
- compare a spec against the current `src/` contract surface and docs
|
|
16
|
-
|
|
17
|
-
# Surface area (actions)
|
|
18
|
-
- locate-rfc
|
|
19
|
-
- compare-against-code
|
|
20
|
-
- assess-contract-completeness
|
|
21
|
-
- classify-gaps
|
|
22
|
-
- produce-review
|
|
23
|
-
|
|
24
|
-
# Core guidance
|
|
25
|
-
1. Read the RFC first, then compare it against the relevant code, docs, and tests.
|
|
26
|
-
2. Separate **spec gaps** from **implementation gaps**.
|
|
27
|
-
3. Check for: problem clarity, scope boundaries, explicit contracts, acceptance criteria, non-goals, and consistency with existing behavior.
|
|
28
|
-
4. Prefer precise feedback that names the missing contract, unclear rule, or inconsistent behavior.
|
|
29
|
-
5. Use the shared review template in `references/rfc-review-template.md` for the final output.
|
|
30
|
-
6. If the RFC is not ready, say exactly what must be clarified before implementation can start.
|
|
31
|
-
7. Classify each blocker as either a **spec gap** or an **implementation contract gap** and stop at that boundary.
|
|
32
|
-
|
|
33
|
-
# Inputs & outputs
|
|
34
|
-
- review-rfc(input: { rfcPath, relatedPaths?, focusAreas? }) -> { verdict, risks, specGaps, implementationGaps, recommendations }
|
|
35
|
-
- compare-against-code(input: { rfcPath, codePaths[] }) -> { matches, mismatches, notes }
|
|
36
|
-
- produce-review(input: { rfcPath, findings[] }) -> { summary, verdict, checklist, nextStep }
|
|
37
|
-
|
|
38
|
-
# Failure handling
|
|
39
|
-
- If the RFC file is missing, stop and report the missing path explicitly.
|
|
40
|
-
- If the RFC is ambiguous, classify each concern as either "spec" or "implementation" instead of blending them.
|
|
41
|
-
- If the review cannot be grounded in the current repo, state that the RFC is not reviewable yet.
|
|
42
|
-
|
|
43
|
-
# Progressive disclosure
|
|
44
|
-
- Keep this file short.
|
|
45
|
-
- Load the reference template only when writing the final review.
|
|
46
|
-
|
|
47
|
-
# References
|
|
48
|
-
- `references/rfc-review-template.md` — standard review format and verdict rubric
|
|
49
|
-
- `references/rfc-review-checklist.md` — questions to apply while reviewing an RFC
|
|
50
|
-
|
|
51
|
-
# License
|
|
52
|
-
Same as repository (MIT).
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# RFC Review Checklist
|
|
2
|
-
|
|
3
|
-
Ask these questions while reviewing:
|
|
4
|
-
|
|
5
|
-
1. Is the problem statement specific and grounded in current failures?
|
|
6
|
-
2. Are non-goals explicit?
|
|
7
|
-
3. Are contracts concrete enough to implement?
|
|
8
|
-
4. Are acceptance criteria testable?
|
|
9
|
-
5. Does the RFC define the source of truth for new fields or behaviors?
|
|
10
|
-
6. Does it match existing code paths and public tool surfaces?
|
|
11
|
-
7. Can each open concern be classified as a spec issue or an implementation issue?
|
|
12
|
-
8. Is the RFC ready to implement without further interpretation?
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# RFC Review Template
|
|
2
|
-
|
|
3
|
-
Use this structure for every RFC review:
|
|
4
|
-
|
|
5
|
-
## Verdict
|
|
6
|
-
- Ready / Needs clarification / Needs implementation contract / Not ready
|
|
7
|
-
|
|
8
|
-
## Summary
|
|
9
|
-
- One short paragraph on the RFC's current quality.
|
|
10
|
-
|
|
11
|
-
## What is good
|
|
12
|
-
- List the strongest parts of the RFC.
|
|
13
|
-
|
|
14
|
-
## Issues
|
|
15
|
-
For each issue, include:
|
|
16
|
-
- **Type:** spec / implementation / implementation contract / doc
|
|
17
|
-
- **Severity:** low / medium / high
|
|
18
|
-
- **Why it matters:** one sentence
|
|
19
|
-
- **Fix:** exact change needed
|
|
20
|
-
|
|
21
|
-
## Missing contract surfaces
|
|
22
|
-
- List any API shapes, response fields, state transitions, or invariants that are still undefined.
|
|
23
|
-
|
|
24
|
-
## Codebase alignment
|
|
25
|
-
- Note whether the RFC matches current `src/`, docs, and tests.
|
|
26
|
-
|
|
27
|
-
## Next step
|
|
28
|
-
- State the smallest next action needed to move the RFC forward.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/007-actionability-resolution-and-executable-target-selection.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/010-verification-stabilization-and-temporal-convergence.md
RENAMED
|
File without changes
|
|
File without changes
|
/package/docs/{rfcs → specs-deltas}/011.1-recovery-contract-types-and-runtime-wiring-spec.md
RENAMED
|
File without changes
|
|
File without changes
|