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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ are released independently from this monorepo.
- Established `PyBLE-dev/PyBLE` as the canonical public monorepo.
- Added public contributor, security, architecture, protocol, and validation
documentation.
- Selected firmware agent `0.4.2` for fresh reproducible builds and
two-profile qualification from the canonical public history.

## App 0.1.0-beta — 2026-07-30

Expand Down
9 changes: 8 additions & 1 deletion app/integration_test/blockly_webview_suite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Part of PyBLE (https://pyble.dev) — see /LICENSE.

import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

Expand Down Expand Up @@ -639,6 +639,13 @@ pixels.write()
reason:
'the real scratch Blockly workspace did not materialize the selected LED GPIO',
);
// A real Android IME can still cover the example action after enterText,
// even when ensureVisible has scrolled its RenderBox into the viewport.
// Close it before asserting hit-testability so the integration gate tests
// the action rather than emulator keyboard-animation timing.
FocusManager.instance.primaryFocus?.unfocus();
await SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
await tester.pumpAndSettle();
final Finder replaceWorkspaceAction = find.byKey(
kBlocksExampleReplaceWorkspaceButtonKey,
);
Expand Down
16 changes: 11 additions & 5 deletions docs/specifications/firmware/TDD.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,11 +613,13 @@ negative tests land while release qualification remains pending. The
maintainer then:

1. runs the engineering baseline on the two exact owned profiles;
2. commits the canonical, redacted raw evidence under
`docs/validation/firmware/oi1/`;
3. derives every profile threshold mechanically with the frozen formulas;
4. commits the populated policy and its evidence SHA-256; and
5. builds the final tagged candidate and reruns verify-mode HIL on both exact
2. runs `assemble-oi1-baseline` against the immutable staged inputs and the
two bench fragments so the tool creates the canonical, redacted evidence
under `docs/validation/firmware/oi1/`, derives every threshold with the
frozen formulas, and atomically updates the policy with its evidence
SHA-256;
3. reviews and commits those mechanically assembled files; and
4. builds the final tagged candidate and reruns verify-mode HIL on both exact
profiles.

A policy has exactly two threshold-bearing profile entries and one deferred
Expand All @@ -632,6 +634,10 @@ matching baseline-evidence digest, and immutable build measurements in
`PYBLE_HIL_RECORDS_V2`. Its runtime observation is pending. Finalization may
fill observations, operator fields, and derived checks only; it must prove the
policy and build portions remain byte/semantically equal to the candidate.
The `assemble-hil-report` helper accepts only bounded per-profile mutable
evidence, copies candidate-frozen fields from the pending report, derives the
footprint/reliability pass from validated observations, and emits the completed
V2 report atomically before finalization.

The validator recomputes image/headroom arithmetic, sample counts, heap
minima, latency maximum, goodput from recorded durations, threshold
Expand Down
53 changes: 49 additions & 4 deletions docs/specifications/firmware/browser-flashing.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# PyBLE ESP32-Family Browser Flashing and Release Bundle

Status: **FROZEN v1.26** · Owner: project maintainer · Frozen:
2026-07-30 (`[docs]`; pre-v1 two-profile release eligibility and explicit
Status: **FROZEN v1.27** · Owner: project maintainer · Frozen:
2026-07-31 (`[docs]`; pre-v1 two-profile release eligibility and explicit
C3 deferral; evidence-derived resource policy and exact HIL V2 records;
pre-policy two-root baseline-input staging;
pre-policy two-root baseline-input staging and mechanical baseline/policy
assembly;
exact-profile manifest, C3 silicon-revision,
license-audit safety, candidate-finalization, and candidate pin-state
amendments, plus the real-tool license-evidence and exact license-catalog
Expand All @@ -18,7 +19,8 @@ markers, generated-header inputs, and direct-object reconciliation, plus
component-owned linked outputs, lexical exact-path validation, nested-build
logical paths, shell-free compiler/linker command receipts, executable
version-matched recovery-command syntax, and the canonical pre-v1 same-origin
publication channel with an optional byte-identical mirror, on the same date)
publication channel with an optional byte-identical mirror, plus bounded
completed-HIL report assembly, on the same date)

This document is the source of truth for the initial browser-provisioning
release bundle. It refines
Expand Down Expand Up @@ -201,6 +203,28 @@ policy, baseline-evidence file, release tag, license evidence, or HIL approval.
Its output is measurement input only: it is not a release candidate and MUST
NOT be accepted by the website or public release validator.

After both exact-profile baseline runs succeed, the release tool MUST provide
an `assemble-oi1-baseline` operation. It accepts the immutable staged baseline
input tree above, exactly two canonical single-profile fragments emitted by
the OI-1 bench, the clean canonical PyBLE proof checkout, and one explicit UTC
`created_at` value. It MUST derive `source_commit` from that checkout's exact
`HEAD` and `firmware_version` from its `versions.lock`; operator-supplied
substitutes for either identity are forbidden. Before writing, it MUST require
the proof checkout to be clean, require exactly one fragment for each profile
regardless of input order, validate every fragment and observation field,
bind each fragment's firmware hash, manifest hash, and build measurements to
the corresponding staged bytes, and mechanically derive all nine thresholds
with the frozen formulas.

The operation MUST canonicalize and create the baseline evidence at
`docs/validation/firmware/oi1/<HEAD>.json`, compute the digest of those exact
bytes, and atomically update `firmware/qualification/oi1-gates.json` with the
exact frozen policy shape and derived thresholds. The baseline path is
no-replace: any existing destination is fatal. The policy update MUST be an
atomic same-directory replacement, and both complete payloads MUST pass the
production baseline/policy validator. This operation is evidence assembly
only; it does not approve a release or mutate staged measurement inputs.

The protected candidate site's build-selected SHA-256 of `release.json` is the
root identity of the candidate exercised during HIL. The completed HIL
evidence MUST record that exact lowercase 64-hex digest. Public finalization
Expand Down Expand Up @@ -1246,6 +1270,27 @@ In a candidate every value is `pending`. In a public report every value is
`passed`; `footprint_reliability` MUST be set by the validator only after the
V2 observations pass, not accepted as independent operator testimony.

The release tool MUST provide an `assemble-hil-report` operation so completing
this contract never requires hand-editing `HIL_REPORT.md`. It accepts one
immutable pending candidate, exactly two JSON completion fragments (one per
profile, in either input order), the canonical qualification checkout, and one
new no-replace output path. A completion fragment contains only the mutable
profile ID, physical board descriptions/capacities, UTC test time,
operator/sign-off and environment strings, the six operator-demonstration
checks other than `footprint_reliability`, one completed `oi1_observation`, and
the redacted console log. It MUST NOT accept status, release identity, artifact
hashes, policy, or build measurements from an operator fragment.

The assembler MUST validate the pending candidate first, compute the exact
candidate `release.json` SHA-256 itself, copy every candidate-frozen field from
the embedded pending records, require all six supplied checks to be `passed`,
validate the observation and every profile threshold, and only then insert
`footprint_reliability: passed` and `status: passed`. It MUST render exactly one
canonical `PYBLE_HIL_RECORDS_V2` marker, validate the completed payload against
the candidate bytes and committed policy, and write the output atomically. It
never mutates the candidate and does not perform public bundle promotion;
`finalize-public` remains the only promotion step.

`oi1_observation` is JSON `null` in a pending candidate. In a completed report
it is an object with exactly:

Expand Down
9 changes: 7 additions & 2 deletions docs/specifications/firmware/specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ This is software-level safety of the IDE/agent, **not** hardware/actuator safety
## 8. Build, versioning & distribution (BLD)

> **FROZEN v1.0 (amended) for the initial ESP32 v1 port (G0 · 2026-07-01;
> browser-release amendments 2026-07-29 and 2026-07-30 · `[docs]`).**
> browser-release amendments 2026-07-29 through 2026-07-31 · `[docs]`).**
> BLD-1…22 are the build/versioning contract build-smith implements. The
> 2026-07-29 amendments tighten BLD-5…8/13/14 and add BLD-17…22 before
> X-10/X-11 code; the 2026-07-30 amendment freezes the two-profile pre-v1
Expand Down Expand Up @@ -619,7 +619,12 @@ This is software-level safety of the IDE/agent, **not** hardware/actuator safety
[`versions.lock`](../../../firmware/versions.lock); verify: build; story:
X-03)*
- **BLD-11** — ESP-IDF MUST be installed from the pin into a **gitignored** directory (not an outer submodule); MicroPython `lib/` deps come from the standard port build. — *(source: PRD §10.9, §17.1; verify: build; story: X-03)*
- **BLD-12** — The firmware agent MUST follow **SemVer** (`MAJOR.MINOR.PATCH`); a backward-incompatible change bumps MAJOR. — *(source: PRD §18.1; verify: build; story: X-11)*
- **BLD-12** — The firmware agent MUST follow **SemVer**
(`MAJOR.MINOR.PATCH`); a backward-incompatible change bumps MAJOR.
`firmware/versions.lock` `[pyble].agent_version` is the canonical agent
version for a source commit, and the importable `pyble.__version__` used by
`DEVICE_INFO`/HELLO MUST equal it exactly. — *(source: PRD §18.1; verify:
build; story: X-11)*
- **BLD-13** — A release MUST make the firmware-agent version, PBLE/1 version,
upstream MicroPython/ESP-IDF versions and commits, PyBLE source commit, image
profile, and artifact hashes recoverable from `DEVICE_INFO`/HELLO,
Expand Down
2 changes: 1 addition & 1 deletion firmware/pyble/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
# S2 onward by their owning engineers — see TDD §10.5. Do NOT add agent logic
# here (that is out of scope for build-smith).

__version__ = "0.4.1" # mirrors versions.lock [pyble] agent_version (SemVer, BLD-12)
__version__ = "0.4.2" # mirrors versions.lock [pyble] agent_version (SemVer, BLD-12)
Loading
Loading