Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f5019d29a0885dfc1420fc219463dc4e6ffb0ecc406120d35b25fe0ea67a3c55
48c36e6c73babe7ec19658ea5820bbf4c9c16d72ba5a58ef21007d2176b89bb3
19 changes: 17 additions & 2 deletions extensions/labwired-vscode/src/board/contextFlags.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,21 @@ function nonEmptyString(value: unknown): string | undefined {
return t.length ? t : undefined;
}

function hasContextProvenance(pack: LabwiredContextPack): boolean {
return !!(
nonEmptyString(pack.source_kind) ||
pack.diagram ||
pack.mapping?.length ||
pack.catalog_hits?.length ||
typeof pack.mint_ok === 'boolean' ||
typeof pack.supported_part_count === 'number'
);
}

function meetsMvc(pack: LabwiredContextPack, board: string | undefined): boolean {
if (typeof pack.design_context_ok === 'boolean') return pack.design_context_ok;
if (hasContextProvenance(pack) && typeof pack.design_context_ok === 'boolean') {
return pack.design_context_ok;
}
if (board) return true;
if (pack.mint_ok === true) return true;
const mapping = pack.mapping ?? [];
Expand All @@ -116,7 +129,9 @@ function hasAnyText(pack: LabwiredContextPack): boolean {
}

function computeTwinBuildable(pack: LabwiredContextPack): boolean {
if (typeof pack.twin_buildable === 'boolean') return pack.twin_buildable;
if (hasContextProvenance(pack) && typeof pack.twin_buildable === 'boolean') {
return pack.twin_buildable;
}
const supported = pack.supported_part_count ?? 0;
return pack.mint_ok === true && supported >= 1;
}
Expand Down
2 changes: 1 addition & 1 deletion skills/develop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Follow this loop: inspect or scaffold → ground → edit → compile → twin c

Rules:

- Call `labwired_context` first. On `empty_context`, use `labwired_list` + `labwired_describe`, then call `labwired_context` again with `pack.board` and `pack.mcu` from that returned catalog board before compiling; for an external circuit, pass the pack returned by `labwired_import`. This second/refreshed `labwired_context` is mandatory and must succeed with `ok: true` and `design_context_ok: true` before any compile. If refreshed context has `ok: false` or `design_context_ok: false`, do not compile. Cite described boards as exact `catalog:board:<returned board>` sources (and components as `catalog:component:<returned type>`). Prefer `labwired_part`, `labwired_datasheet`, or `labwired_search` for other hardware facts. If those knowledge tools are unavailable, grounded existing project files, SDK headers, SVD files, and schematics or netlists are valid fallback sources; cite the source used. Label missing-source deductions as inferred or as a gap, and never invent pins, peripherals, addresses, clocks, timing, or registers.
- Call `labwired_context` first. On `empty_context`, use `labwired_list` + `labwired_describe`, then call `labwired_context` again with `pack.board` and `pack.mcu` from that returned catalog board before compiling; for an external circuit, pass the pack returned by `labwired_import`. A refreshed pack contains only resolved inputs—never copy stale `false`, negative-status, or other output fields into it. This second/refreshed `labwired_context` is mandatory and must succeed with `ok: true` and `design_context_ok: true` before any compile. If refreshed context has `ok: false` or `design_context_ok: false`, do not compile. Cite described boards as exact `catalog:board:<returned board>` sources (and components as `catalog:component:<returned type>`). Prefer `labwired_part`, `labwired_datasheet`, or `labwired_search` for other hardware facts. If those knowledge tools are unavailable, grounded existing project files, SDK headers, SVD files, and schematics or netlists are valid fallback sources; cite the source used. Label missing-source deductions as inferred or as a gap, and never invent pins, peripherals, addresses, clocks, timing, or registers.
- Preserve an existing project's structure. For greenfield work, create the smallest conventional project that satisfies the request.
- Compile with `labwired_compile` or the project's existing compile command.
- Prefer a reviewed, checked-in safe `.labwired/hardware.json` hardware profile
Expand Down
5 changes: 5 additions & 0 deletions tests/develop-agent-e2e-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ command_line="$(bash "$E2E" --print-command /tmp/project 'fixed prompt')"
[[ "$command_line" != *"labwired-fast"* ]]
grep -q 'LABWIRED_DEVELOP_SCENARIO_TIMEOUT_SECONDS' "$E2E"
grep -q 'subprocess.run' "$E2E"
grep -q "printf -v cleanup_cmd" "$E2E"
if grep -q "trap 'rm -rf \"\$work\"'" "$E2E"; then
echo "FAIL cleanup trap references expired local work variable" >&2
exit 1
fi

echo "ok develop-agent-e2e contract"
5 changes: 3 additions & 2 deletions tests/develop-agent-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ prepare_project() {

run_all() {
check_prerequisites
local work evidence
local work evidence cleanup_cmd
work="$(mktemp -d "${TMPDIR:-/tmp}/labwired-develop-agent.XXXXXX")"
evidence="${LABWIRED_DEVELOP_EVIDENCE_DIR:-$work/evidence}"
mkdir -p "$evidence"
chmod 700 "$evidence"
trap 'rm -rf "$work"' EXIT
printf -v cleanup_cmd 'rm -rf -- %q' "$work"
trap "$cleanup_cmd" EXIT

python3 - "$FIX/prompts.json" "$work/prompts.tsv" <<'PY'
import json, sys
Expand Down
12 changes: 12 additions & 0 deletions tests/develop-agent-oracle-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def run(name):
assert opencode_bundle["final_claim"] == "model_verified"
assert opencode_bundle["source_citations"] == ["catalog:board:esp32-c3-supermini"]

max_steps_verified = subprocess.run(
[sys.executable, str(ORACLE), "validate", str(FIX / "opencode-max-steps-verified.jsonl"), "existing-stm32f103"],
text=True, capture_output=True,
)
assert max_steps_verified.returncode == 0, max_steps_verified.stderr

refreshed = run("empty-refresh-valid.jsonl")
assert refreshed.returncode == 0, refreshed.stderr

Expand Down Expand Up @@ -111,6 +117,12 @@ def run(name):
assert ambiguous.returncode != 0
assert "explicit failed compile" in ambiguous.stderr.lower(), ambiguous.stderr

opencode_recovery = subprocess.run(
[sys.executable, str(ORACLE), "validate", str(FIX / "opencode-recovery-edit-valid.jsonl"), "compile-recovery-esp32c3"],
text=True, capture_output=True,
)
assert opencode_recovery.returncode == 0, opencode_recovery.stderr

secrets = run("secrets.jsonl")
assert secrets.returncode == 0, secrets.stderr
for forbidden in ("secret-token-value", "signed-secret", "person@example.com", "Bearer"):
Expand Down
11 changes: 10 additions & 1 deletion tests/develop-agent-oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ def typed_record(result: dict, key: str, expected_type: str) -> dict | None:


def phase_outcome(event: dict, phase: str) -> bool | None:
# OpenCode's built-in edit tool returns a fixed success sentence rather
# than a JSON domain envelope. Accept only that exact canonical response;
# arbitrary prose or model-supplied command text remains non-evidence.
if phase == "edit" and tool_name(event).lower() == "edit" and outcome(event) is True:
payloads = returned_payloads(event)
if payloads == ["Edit applied successfully."]:
return True
domain = domain_outcome(event)
if domain is False:
return False
Expand Down Expand Up @@ -240,7 +247,9 @@ def phase_outcome(event: dict, phase: str) -> bool | None:
clauses = result.get("oracle_results")
if (
result.get("proven") is True
and result.get("stop_reason") == "assertions_passed"
and result.get("gaps") == []
and result.get("run_status") in (None, "pass")
and result.get("stop_reason") in {"assertions_passed", "max_steps"}
and isinstance(clauses, list)
and clauses
and all(isinstance(clause, dict) and clause.get("passed") is True for clause in clauses)
Expand Down
32 changes: 32 additions & 0 deletions tests/develop-agent-wiring-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,38 @@
assert 'source must be exactly catalog:board:esp32-c3-supermini' in prompts["greenfield-esp32c3"]
assert 'source must be exactly catalog:board:stm32f103-blinky' in prompts["existing-stm32f103"]
assert 'source must be exactly catalog:board:esp32-c3-supermini' in prompts["compile-recovery-esp32c3"]
stm32_prompt = prompts["existing-stm32f103"]
for required in (
"edit only the existing src/main.cpp in place",
"do not add, remove, or rename files",
"leave platformio.ini byte-identical",
):
assert required in stm32_prompt
recovery_prompt = prompts["compile-recovery-esp32c3"]
for required in (
"The project already contains the deliberate error in src/main.cpp; do not overwrite or replace it before the first compile.",
"After the failed compile, use the edit tool on src/main.cpp for one focused repair",
"compile the exact repaired file content",
):
assert required in recovery_prompt
partial_prompt = prompts["partial-led-wifi"]
for required in (
"upload the complete project with labwired_put_source",
"compile its returned source_tree_ref",
"target esp32-c3-supermini and the returned flash_image_refs",
"serial contains `Connecting to Wi-Fi` plus GPIO8 toggled",
'refresh context with exactly pack.board `esp32-c3-supermini` and pack.mcu `esp32c3`',
"do not include false context flags",
):
assert required in partial_prompt
unsupported_prompt = prompts["unsupported-custom-board"]
for required in (
"nucleo-f401re only as a compiler surrogate",
"attempt labwired_verify with target custom-board",
"Physical confirmation is still required",
'"hardware_sensitive_facts":[{"fact":"PA5 is the surrogate board LED pin","source":"catalog:board:nucleo-f401re"}]',
):
assert required in unsupported_prompt
assert 'source must be exactly catalog:board:esp32-c3-supermini' in prompts["partial-led-wifi"]
for name in ("greenfield-esp32c3", "existing-stm32f103", "compile-recovery-esp32c3", "partial-led-wifi"):
assert "GPIO oracle state must be toggled" in prompts[name]
Expand Down
1 change: 1 addition & 0 deletions tests/develop-skill.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fi
need 'labwired_context' 'context tool'
need 'empty[_ -]?context.*(resolve|import|select|labwired_list|labwired_describe).*(labwired_context.*(again|re-run)|re-run.*labwired_context).*before.*compil' 'empty context must be resolved and refreshed before compile'
need 'labwired_context.*pack.*board.*mcu' 'catalog board context is refreshed with an explicit pack'
need '(refresh|refreshed).*pack.*(only|resolved inputs).*(never|do not).*(false|negative|status|output)' 'context refresh excludes stale negative output fields'
need '(second|refreshed).*(labwired_context|context).*(must|mandatory).*(succeed|ok|design_context_ok).*before.*compil' 'refreshed context success is a hard compile gate'
need '(ok.*false|design_context_ok.*false).*(do not|never|must not).*compil' 'failed context blocks compile'
need 'catalog:board:.*returned.*board|returned.*board.*catalog:board:' 'catalog board citations use the canonical returned id'
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/develop-agent/empty-refresh-valid.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
{"type":"tool_use","part":{"id":"r2","tool":"labwired_describe","state":{"status":"completed","input":{"id":"esp32-c3-supermini"},"output":"{\"board\":\"esp32-c3-supermini\",\"chip\":\"esp32c3\"}"}}}
{"type":"tool_use","part":{"id":"r3","tool":"labwired_context","state":{"status":"completed","input":{"pack":{"board":"esp32-c3-supermini","mcu":"esp32c3"}},"output":"{\"ok\":true,\"design_context_ok\":true,\"board\":\"esp32-c3-supermini\",\"mcu\":\"esp32c3\"}"}}}
{"type":"tool_use","part":{"id":"r4","tool":"labwired_compile","state":{"status":"completed","input":{},"output":"{\"ok\":true,\"runnable\":true,\"flash_image_refs\":[{\"offset\":0,\"ref\":\"sha256:firmware\"}]}"}}}
{"type":"tool_use","part":{"id":"r5","tool":"labwired_verify","state":{"status":"completed","input":{},"output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"stop_reason\":\"assertions_passed\",\"oracle_results\":[{\"passed\":true,\"detail\":\"serial contains alive\"}]}"}}}
{"type":"tool_use","part":{"id":"r5","tool":"labwired_verify","state":{"status":"completed","input":{},"output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"gaps\":[],\"run_status\":\"pass\",\"stop_reason\":\"assertions_passed\",\"oracle_results\":[{\"passed\":true,\"detail\":\"serial contains alive\"}]}"}}}
{"type":"text","part":{"id":"r6","text":"{\"claim\":\"model_verified\"}"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{"type":"tool_use","part":{"id":"e1","tool":"labwired_context","state":{"status":"completed","output":"{\"ok\":true,\"design_context_ok\":true,\"board\":\"stm32f103-blinky\",\"mcu\":\"stm32f103\"}"}}}
{"type":"tool_use","part":{"id":"e2","tool":"labwired_describe","state":{"status":"completed","output":"{\"board\":\"stm32f103-blinky\"}"}}}
{"type":"tool_use","part":{"id":"e3","tool":"labwired_compile","state":{"status":"completed","output":"{\"ok\":true,\"firmware_ref\":\"sha256:firmware-fixture\",\"runnable\":true}"}}}
{"type":"tool_use","part":{"id":"e4","tool":"labwired_verify","state":{"status":"completed","output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"gaps\":[],\"run_status\":\"pass\",\"stop_reason\":\"max_steps\",\"oracle_results\":[{\"kind\":\"gpio\",\"passed\":true,\"detail\":\"PC13 toggled (gpio evidence)\"}]}"}}}
{"type":"text","part":{"text":"{\"claim\":\"model_verified\",\"hardware_sensitive_facts\":[{\"fact\":\"PC13 is the board heartbeat LED pin\",\"source\":\"catalog:board:stm32f103-blinky\"}]}"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{"type":"tool_use","part":{"id":"r1","tool":"labwired_context","state":{"status":"completed","output":"{\"ok\":true,\"design_context_ok\":true,\"board\":\"esp32-c3-supermini\",\"mcu\":\"esp32c3\"}"}}}
{"type":"tool_use","part":{"id":"r2","tool":"labwired_describe","state":{"status":"completed","output":"{\"board\":\"esp32-c3-supermini\"}"}}}
{"type":"tool_use","part":{"id":"r3","tool":"labwired_compile","state":{"status":"error","error":"compiler rejected source"}}}
{"type":"tool_use","part":{"id":"r4","tool":"edit","state":{"status":"completed","output":"Edit applied successfully."}}}
{"type":"tool_use","part":{"id":"r5","tool":"labwired_compile","state":{"status":"completed","output":"{\"ok\":true,\"runnable\":true,\"firmware_ref\":\"sha256:repaired-firmware\"}"}}}
{"type":"tool_use","part":{"id":"r6","tool":"labwired_verify","state":{"status":"completed","output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"gaps\":[],\"run_status\":\"pass\",\"stop_reason\":\"assertions_passed\",\"oracle_results\":[{\"kind\":\"serial\",\"passed\":true,\"detail\":\"serial contains alive\"},{\"kind\":\"gpio\",\"passed\":true,\"detail\":\"GPIO8 toggled\"}]}"}}}
{"type":"text","part":{"text":"{\"claim\":\"model_verified\",\"hardware_sensitive_facts\":[{\"fact\":\"GPIO8 is the active-low board LED pin\",\"source\":\"catalog:board:esp32-c3-supermini\"}]}"}}
2 changes: 1 addition & 1 deletion tests/fixtures/develop-agent/opencode-valid.jsonl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{"type":"tool_use","part":{"id":"oc1","tool":"labwired_context","state":{"status":"completed","input":{},"output":"{\"ok\":true,\"mode\":\"design_only\",\"design_context_ok\":true,\"twin_buildable\":false,\"board\":\"esp32-c3-supermini\",\"mcu\":\"esp32c3\"}"}}}
{"type":"tool_use","part":{"id":"oc2","tool":"labwired_describe","state":{"status":"completed","input":{"id":"esp32-c3-supermini"},"output":"{\"board\":\"esp32-c3-supermini\",\"chip\":\"esp32c3\",\"description\":\"Built-in user LED on GPIO8 (active-low)\"}"}}}
{"type":"tool_use","part":{"id":"oc3","tool":"labwired_compile","state":{"status":"completed","input":{"project":"."},"output":"{\"ok\":true,\"runnable\":true,\"flash_image_refs\":[{\"offset\":0,\"ref\":\"sha256:firmware-fixture\",\"blob_path\":\"/v1/blobs/sha256/firmware-fixture\"}]}"}}}
{"type":"tool_use","part":{"id":"oc4","tool":"labwired_verify","state":{"status":"completed","input":{"firmware":"sha256:firmware-fixture"},"output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"stop_reason\":\"assertions_passed\",\"oracle_results\":[{\"passed\":true,\"detail\":\"serial contains alive\"}]}"}}}
{"type":"tool_use","part":{"id":"oc4","tool":"labwired_verify","state":{"status":"completed","input":{"firmware":"sha256:firmware-fixture"},"output":"{\"ok\":true,\"status\":\"model_verified\",\"proven\":true,\"gaps\":[],\"run_status\":\"pass\",\"stop_reason\":\"assertions_passed\",\"oracle_results\":[{\"passed\":true,\"detail\":\"serial contains alive\"}]}"}}}
{"type":"text","part":{"id":"oc5","type":"text","text":"Final claim: model_verified"}}
Loading