@protoboardai/ext-release 0.0.0-stage → 0.2.1
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/LICENSE +202 -0
- package/NOTICE +13 -0
- package/README.md +290 -2
- package/evals/release-after-hand-edit/case.json +59 -0
- package/lib/bom.ts +444 -0
- package/lib/collect.ts +100 -0
- package/lib/config.ts +45 -0
- package/lib/cutlist.ts +70 -0
- package/lib/documents.ts +287 -0
- package/lib/glb.ts +85 -0
- package/lib/helpers.ts +49 -0
- package/lib/html.ts +126 -0
- package/lib/manifest.ts +159 -0
- package/lib/offers.ts +123 -0
- package/lib/preconditions.ts +141 -0
- package/lib/product.ts +64 -0
- package/lib/release.ts +279 -0
- package/lib/render.ts +203 -0
- package/lib/sections/baseline.ts +164 -0
- package/lib/sections/bom.ts +101 -0
- package/lib/sections/model.ts +78 -0
- package/lib/sections/testplan.ts +76 -0
- package/lib/sections/tests.ts +630 -0
- package/lib/sections/workorder.ts +369 -0
- package/lib/testing.ts +43 -0
- package/lib/types.ts +251 -0
- package/lib/units.ts +141 -0
- package/lib/util.ts +31 -0
- package/ops/release.ts +21 -0
- package/ops/sections.ts +23 -0
- package/package.json +52 -4
- package/protoboard-extension.json +81 -0
- package/skills/release/SKILL.md +191 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Protoboard Stack: @protoboardai/ext-release
|
|
2
|
+
Copyright 2026 Delta Robotics, Inc.
|
|
3
|
+
|
|
4
|
+
This product includes software developed at Delta Robotics, Inc.
|
|
5
|
+
(https://deltaroboticsinc.com).
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License").
|
|
8
|
+
See the LICENSE file in this package for full terms.
|
|
9
|
+
|
|
10
|
+
"Protoboard", the Protoboard logo and the "0" mark are trademarks of
|
|
11
|
+
Delta Robotics, Inc. The License grants no rights to them (section 6);
|
|
12
|
+
see TRADEMARKS.md in the Protoboard Stack repository
|
|
13
|
+
(https://github.com/Delta-Robotics-Inc/protoboard-stack).
|
package/README.md
CHANGED
|
@@ -1,3 +1,291 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @protoboardai/ext-release
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`protoboard release` and the Manufacturing Package
|
|
4
|
+
([releases, package, SOPs and evals](../../docs/specs/release-package-and-evals.md)
|
|
5
|
+
§1–§3). A release refuses a project that is not clean, then
|
|
6
|
+
writes everything needed to order, make, assemble and check the product to
|
|
7
|
+
`release/<version>/`, laid out by unit, with a manifest that a rebuild in a
|
|
8
|
+
clean environment reproduces byte for byte. It never orders, uploads or
|
|
9
|
+
prints anything, and never calls a service.
|
|
10
|
+
|
|
11
|
+
| Kind | Id | What |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Operation | `release` (export; tool `chrome`) | The package; inputs `version` (default: the root module's `version`), `allow-dirty`, `check` (preconditions only). `protoboard release [--version <v>] [--allow-dirty] [--check]` runs it. |
|
|
14
|
+
| Operations | `package:spec`, `package:bom`, `package:work-order`, `package:assembly`, `package:test-plan` (pure) | The release's own section content, from the model and the collected files: identity and interfaces with ratings; module breakdown and ordering list; ordering checklist, fabrication orders, operations and kitting; plain assembly steps when nothing richer is installed; the basic test plan |
|
|
15
|
+
| Skill | `release` | The SOP: choose units, clear the preconditions, release, inspect |
|
|
16
|
+
| Tool | `chrome` | Headless Chrome for the unit figures and the PDFs (@protoboardai/ext-docs' renderer) |
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// protoboard.config.ts (or the default export of release.config.ts)
|
|
20
|
+
extensions: [..., "@protoboardai/ext-release"],
|
|
21
|
+
release: {
|
|
22
|
+
units: ["stack", "quadcopter-5in-arm"], // instance paths or module ids; the root is always a unit
|
|
23
|
+
build: 1, // products to build: ordering quantities × build
|
|
24
|
+
spares: { fastener: { percent: 20, min: 4 } }, // by part id, tag or category; overrides the defaults
|
|
25
|
+
},
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## The package
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
release/<version>/
|
|
32
|
+
00-overview.pdf the cover, then the product page (what it is, features, functions, requirements with how
|
|
33
|
+
each is shown, from docs/product.json), units, order of work, index of files, traceability appendix
|
|
34
|
+
index.html all of it, navigable by unit and by section (assets/ holds its figures and Kode Mono)
|
|
35
|
+
manifest.json protoboard.release/v0
|
|
36
|
+
system/ the root unit: final assembly
|
|
37
|
+
spec-sheet.pdf bom.pdf bom.csv ordering-list.csv work-order.pdf assembly-guide.pdf test-plan.pdf
|
|
38
|
+
files/model/uhd-bundle.json the released model (artifact bases relative to the project)
|
|
39
|
+
files/firmware/… configurations other extensions contribute (ext-drone: betaflight-cli.txt)
|
|
40
|
+
units/<unit>/ one per release.units entry, same layout (no ordering list)
|
|
41
|
+
files/boards/<module>/ circuit: gerber/ (layers and drill), bom.csv, pnp.csv, board.step, board.glb
|
|
42
|
+
files/mechanical/<module>/ cad: <module>.step and the process exports (<module>.stl, .3mf, .dxf)
|
|
43
|
+
files/harnesses/<module>/ cad on a cable: routed <module>.step and .glb; cut-list.csv from the model
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Units.** An instance path makes that instance a unit (folder: the path with
|
|
47
|
+
`/` as `.`); a module id makes every instance of the module one unit, built
|
|
48
|
+
once per instance. Every other instance folds into the nearest unit above it.
|
|
49
|
+
A unit inside another is built first; the root (`system/`, final assembly) is
|
|
50
|
+
last. A unit named by path may not sit inside one build of a module unit that
|
|
51
|
+
is built several times (the builds would differ). Files of a custom module go
|
|
52
|
+
to every unit that builds it.
|
|
53
|
+
|
|
54
|
+
**Files** come only from the providers' standard outputs (`<module>/out/`),
|
|
55
|
+
never from native files, renamed as above. The cut list is derived from the
|
|
56
|
+
cable's UHD facts (`performance` kind cable, `cable_build`, the conductor
|
|
57
|
+
link between its ends); a cable the unit makes or cuts without a cad
|
|
58
|
+
provider gets `files/harnesses/<module>/cut-list.csv` too, and the work order
|
|
59
|
+
and test plan name a cut list only when the package holds it.
|
|
60
|
+
|
|
61
|
+
## Preconditions
|
|
62
|
+
|
|
63
|
+
Nothing is written unless all of these hold (`--check` reports them):
|
|
64
|
+
|
|
65
|
+
| Code | Rule |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `REL_CHECK_ERRORS` | `protoboard check` has no errors. |
|
|
68
|
+
| `REL_STALE` | No provider finding ends in `_STALE` (warning or error). Every bound module has a recorded `<capability>:export` made with the current config file (digest) and provider, and, when the record has it, the current module (`capability.moduleDigest`, recorded by core since Stack 0.1). `out/` holds exactly the files that record lists, with its digests. Every file any operation record wrote is still the bytes its latest writer wrote (earlier packages under `release/` excepted: a release replaces its folder). |
|
|
69
|
+
| `REL_FRESHNESS_UNKNOWN` | No provider check was skipped: a skipped check cannot show its outputs are current. |
|
|
70
|
+
| `REL_NOT_EXPORTED` | Every bound module has standard outputs. |
|
|
71
|
+
| `REL_DIRTY` | In a git work tree, no uncommitted changes outside `release/` and `operations/` (`--allow-dirty` records them in the manifest instead). |
|
|
72
|
+
| `REL_UNIT_INVALID`, `REL_CONFIG_INVALID`, `REL_NO_VERSION` | The configuration resolves. |
|
|
73
|
+
| any error a section returns | e.g. ext-drone's `DRONE_CLI_STALE` when the flight-controller configuration no longer matches the model. |
|
|
74
|
+
|
|
75
|
+
## Section contract
|
|
76
|
+
|
|
77
|
+
An extension contributes to a section by declaring an operation
|
|
78
|
+
`package:<section>` (`spec`, `bom`, `work-order`, `assembly`, `test-plan`;
|
|
79
|
+
effect `pure`). The release calls it once per unit, the others in
|
|
80
|
+
configuration order and the release's own entry last (so its work order can
|
|
81
|
+
number what they contribute), and renders what it returns into that unit's
|
|
82
|
+
PDF and `index.html`, the release's own content first.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import type { SectionFn } from "@protoboardai/ext-release";
|
|
86
|
+
|
|
87
|
+
export const workOrder: SectionFn = ({ project, unit, units, release, files }) => {
|
|
88
|
+
if (unit.kind !== "root") return undefined; // nothing for this unit
|
|
89
|
+
return {
|
|
90
|
+
title: "Flight controller configuration", // its own numbered part of the document; omit to join the main body
|
|
91
|
+
order: 20,
|
|
92
|
+
blocks: [
|
|
93
|
+
{ kind: "paragraph", text: "…" },
|
|
94
|
+
{ kind: "table", columns: [{ key: "item", label: "Item" }], rows: [{ item: "…" }] },
|
|
95
|
+
],
|
|
96
|
+
// work order only: operations the release numbers with its own (OP 10, 20, …)
|
|
97
|
+
operations: [{ stage: "configure", title: "Load the configuration", actions: ["…"], test: "…" }],
|
|
98
|
+
files: [{ path: "files/firmware/betaflight-cli.txt", from: "firmware/betaflight/cli.txt", role: "firmware-config" }],
|
|
99
|
+
diagnostics: [], // an error stops the release
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Blocks: `heading`, `paragraph` (`keep` holds it on the page of the next
|
|
105
|
+
block), `list`, `keyvalues`, `figures` (key figures), `table` (with `{ group
|
|
106
|
+
}` rows; `nowrap` columns), `note` (tip, caution, warning), `steps` (an id,
|
|
107
|
+
actions, parts with thumbnails and letters, a figure with callouts, a table,
|
|
108
|
+
tools, materials, torque, inspection, the test that follows, notes, trace),
|
|
109
|
+
`image` (with lettered `callouts`; `size: "small"`) and `kit` (a grid of
|
|
110
|
+
items with thumbnails). Files go under the unit folder (`files/…` for
|
|
111
|
+
manufacturing files; a unit-level data file such as `bom.csv`); a `from`
|
|
112
|
+
path is copied from the project, and the manifest traces it to the operation
|
|
113
|
+
record that wrote it. Types: `lib/types.ts`.
|
|
114
|
+
|
|
115
|
+
The context also carries:
|
|
116
|
+
|
|
117
|
+
- `figure(name, spec)`: renders an ext-docs `RenderSpec` (line art or
|
|
118
|
+
shaded, from served GLB paths, `project/<path>`) with the release's Chrome
|
|
119
|
+
into `assets/images/<name>.png` and returns its package path, size and
|
|
120
|
+
anchor positions (for callouts). A name is rendered once per release; the
|
|
121
|
+
same name with another spec is an error. Figures are rendered before the
|
|
122
|
+
documents, one Chrome at a time.
|
|
123
|
+
- `contributors`: the extensions contributing to this section, so a
|
|
124
|
+
baseline can step aside (the release's plain assembly steps give way to
|
|
125
|
+
ext-docs' illustrated ones).
|
|
126
|
+
- `operations`: for the release's own work order, the operations the others
|
|
127
|
+
returned. A `configure` operation (flash firmware, load a configuration)
|
|
128
|
+
runs after the assembly steps, between the power-up checks and the rest of
|
|
129
|
+
the bench test, and so before the steps that must follow the test.
|
|
130
|
+
|
|
131
|
+
`@protoboardai/ext-release/helpers` gives contributors what the release
|
|
132
|
+
knows: the custom modules a unit builds (`unitModules`, `providerModules`),
|
|
133
|
+
how many per build (`perBuild`), the files collected for one
|
|
134
|
+
(`moduleFiles`), its JSON standard outputs (`readOutput`), a GLB's size
|
|
135
|
+
(`glbBox`), the cut list and the bill of materials.
|
|
136
|
+
`@protoboardai/ext-release/testing` builds the units and contexts of a project
|
|
137
|
+
for a contributor's tests, recording figures instead of rendering them.
|
|
138
|
+
|
|
139
|
+
## Bills of materials (PK2)
|
|
140
|
+
|
|
141
|
+
- **`bom.csv` / `bom.pdf`** per unit: one build's module tree (sub-assembly →
|
|
142
|
+
board → components), one row per instance with quantity per build and per
|
|
143
|
+
product, make/buy, manufacturer, MPN, tier and origin. A board's components
|
|
144
|
+
are its UHD children; the circuit provider's `out/bom.csv` is merged by its
|
|
145
|
+
`instance` column for value, package and MPN where the model has none.
|
|
146
|
+
- **`system/ordering-list.csv`**: every item to obtain for the whole product,
|
|
147
|
+
one line per part. Quantities come from the instance tree (independent of
|
|
148
|
+
the units). `needed = per product × build`; `spares = max(ceil(needed ×
|
|
149
|
+
percent / 100), min)` by the first matching rule: `release.spares[part id]`,
|
|
150
|
+
`release.spares[tag or category]`, then the defaults: mechanical hardware
|
|
151
|
+
(category `hardware` or tag `fastener`) 10 %, at least 2; components placed
|
|
152
|
+
on a board 5 %, at least 2; nothing else. `order` rounds `needed + spares`
|
|
153
|
+
up to whole packs (the offer's own, else the `pack_quantity` the model
|
|
154
|
+
states) and the chosen offer's minimum, and goes up to a later price break
|
|
155
|
+
where that costs less in all (`offer_flag` then says what the smaller
|
|
156
|
+
order would have cost). Parts that ship with another (`included_with`) are listed with
|
|
157
|
+
order 0; custom parts, boards and cables not included with a part are
|
|
158
|
+
`fabricate` lines.
|
|
159
|
+
- **Offers, offline only**: catalog offers vendored by `protoboard parts
|
|
160
|
+
offers` into `parts/offers.json` (`protoboard.offers/v0`), sourcing evidence
|
|
161
|
+
in `parts/evidence/<id>/sourcing.json` (@protoboardai/ext-sourcing: price
|
|
162
|
+
breaks, stock, lead time, lifecycle, alternates), and as a last resort a
|
|
163
|
+
supplier listing the model cites (a `usage_note` starting "Supplier
|
|
164
|
+
listing:"; where to buy, no price). Each catalog offer names the MPN it is
|
|
165
|
+
for, so a series passive's values are priced by their own offers. A value
|
|
166
|
+
with no offer for its own MPN stays unpriced; it is never priced by
|
|
167
|
+
another value's offer.
|
|
168
|
+
- **One quote, the bill of materials'** (docs/specs/bom.md §3.8). A
|
|
169
|
+
bought line is quoted by the model the bill of materials uses
|
|
170
|
+
(ext-sourcing `quoteHeldLines`), so a release and `protoboard bom` agree
|
|
171
|
+
on supplier and price for the same pieces: the offer a person chose for
|
|
172
|
+
the line in `parts/bom.json` (flagged "chosen in the bill of materials"),
|
|
173
|
+
else, in order, an offer with a price; not a full reel for fewer than a
|
|
174
|
+
reel (that line stays unpriced and says so in `offer_flag`, the reel
|
|
175
|
+
listed in `offer_alternatives`; a reel the quantity fills is quoted and
|
|
176
|
+
flagged); stock that covers the order or is not stated; a current price
|
|
177
|
+
before a stale one; the lowest cost of the order in dollars. Every price
|
|
178
|
+
is in US dollars: `unit_price`, `currency` (`USD`) and `extended`. An
|
|
179
|
+
offer in another currency is converted with the project's dated rate
|
|
180
|
+
table (`parts/fx-rates.json`, else the Stack's) and marked:
|
|
181
|
+
`supplier_unit_price`, `supplier_currency`, `fx_rate` and `fx_date` carry
|
|
182
|
+
the supplier's own price and the rate, `offer_flag` says "converted from
|
|
183
|
+
EUR at 1.1177 (2026-10-07)", and the PDF's price reads "(from EUR)". An
|
|
184
|
+
offer in a currency the table lacks is not quoted. The cost estimate is
|
|
185
|
+
the sum of the lines; its note names the currencies converted and the
|
|
186
|
+
table's date, and it says how many bought lines are priced. `currency` in
|
|
187
|
+
protoboard.config.ts no longer changes a release's quotes. The only
|
|
188
|
+
difference from the bill of materials is the quantity: a release adds
|
|
189
|
+
spares.
|
|
190
|
+
|
|
191
|
+
## Work order, assembly guide and test plan (PK3–PK5)
|
|
192
|
+
|
|
193
|
+
The three documents follow one sequence: the unit's assembly plan from
|
|
194
|
+
`@protoboardai/ext-docs/plan` (the doc spec's steps, then automatic ones,
|
|
195
|
+
scoped to the links the unit makes; a board's nets are its copper, not
|
|
196
|
+
steps). The release's own content:
|
|
197
|
+
|
|
198
|
+
- **Work order.** Ordering checklist from the ordering list (the whole
|
|
199
|
+
product on the root, the unit's own items elsewhere), with supplier, lead
|
|
200
|
+
time and minimum order where an offer or sourcing record states them and
|
|
201
|
+
"unknown" otherwise. Fabrication orders, one line per made part, pointing
|
|
202
|
+
at the provider's details below. Operations, numbered OP 10, 20, …: kit and
|
|
203
|
+
incoming inspection (first-article ids), board assembly, cutting and
|
|
204
|
+
preparing cables whose installed length is shorter than supplied, one
|
|
205
|
+
operation per assembly step (parts, tools, materials, torque, an inspection
|
|
206
|
+
point and the joint check that follows), then the bench test. With
|
|
207
|
+
contributed `configure` operations the test is split around them: power-up
|
|
208
|
+
checks (before power, power-up, rails, current), the configuration, then
|
|
209
|
+
the checks that need firmware (programming, bus scans, smoke tests). Steps
|
|
210
|
+
that must follow the test come next: a step that fits propellers, or one
|
|
211
|
+
the doc spec marks `after: "test"` (the quadcopter's battery); then a final
|
|
212
|
+
check with the functional checks on the finished product. A sub-unit with
|
|
213
|
+
such a step gets a `REL_AFTER_TEST_IN_SUBUNIT` warning, since it is built
|
|
214
|
+
before the product's own test. Kitting: what
|
|
215
|
+
each operation consumes and where it comes from (ordering-list line,
|
|
216
|
+
fabrication order, sub-unit folder, the part it ships with).
|
|
217
|
+
- **Assembly guide.** Contributed by ext-docs (illustrated steps); the
|
|
218
|
+
release's plain steps only when no other extension contributes, from the
|
|
219
|
+
same plan as the work order (its "Assembly guide step N" is step N). A part
|
|
220
|
+
whose connectors ship loose (UHD `connector` trait `supplied: "loose"`, a
|
|
221
|
+
shield's header strips) gets a soldering step first, with a solder-joint
|
|
222
|
+
check. A mate the scene could not place (no geometry) lowers the
|
|
223
|
+
component-side part onto the structure-side one (the shield onto the UNO).
|
|
224
|
+
- **Test plan** (basic v0.1, marked so). First-article inspection of each
|
|
225
|
+
part designed in the project: overall size from its GLB, thickness, hole
|
|
226
|
+
patterns from its bolt-pattern interfaces, seal glands, features present;
|
|
227
|
+
boards bare and assembled; cables' length and conductor order. One joint
|
|
228
|
+
check per assembly step. Bring-up of what the unit powers: before power,
|
|
229
|
+
power-up current-limited (a battery's voltage in its place, a board's
|
|
230
|
+
undriven edge nets, power inputs nothing in the unit feeds), each rail at
|
|
231
|
+
its design voltage (board nets, regulated outputs of purchased modules),
|
|
232
|
+
current against the model's supply budget (`supply_budget`) and
|
|
233
|
+
`docs/power-budget.md` when the project has one, programming and boot on
|
|
234
|
+
the reachable USB/SWD/JTAG ports, an I2C scan per controller with the
|
|
235
|
+
addresses the parts state, and a smoke test per signal link. Functional
|
|
236
|
+
checks, one per requirement of `docs/brief.md` (ID tables, requirement
|
|
237
|
+
tables, top-level and requirement-section bullets), on the root; a
|
|
238
|
+
table's How, Pass and Record columns give the check's method and
|
|
239
|
+
criterion, otherwise they follow the requirement's wording. A rail that
|
|
240
|
+
passes an input through (the same voltage range as one of the part's
|
|
241
|
+
inputs) reads the supply's voltage. An input the model marks as left
|
|
242
|
+
unconnected on purpose (UHD `ChildModuleRef.unconnected`, with a reason)
|
|
243
|
+
gets no bench supply; when a rail can only read such an input (a motor
|
|
244
|
+
shield's outputs with its motor terminal open), a "Temporary supply" check
|
|
245
|
+
on that input comes first and those rails are measured with it on. The
|
|
246
|
+
I2C scan lists every address a device answers at: its `i2c_address` and
|
|
247
|
+
each `i2c_address_<name>` it states (a PCA9685's all-call 0x70). Project checks (`release.checks`) join
|
|
248
|
+
the bring-up, numbered power-phase first. Records to
|
|
249
|
+
capture, and `files/test/test-record.csv` with one row per check.
|
|
250
|
+
- **Builds that differ.** A unit named by module id is built once per
|
|
251
|
+
instance from one plan. Where the instances differ in what their parent
|
|
252
|
+
sets (`spin`, `overrides`), the plan names the value per build where a step
|
|
253
|
+
depends on it (the propeller's hand by the motor's spin at each position),
|
|
254
|
+
the work order and guide say how the builds differ, and a difference no
|
|
255
|
+
step accounts for is a `REL_BUILDS_DIFFER` warning.
|
|
256
|
+
- **Counts.** A wiring step's actions and its joint check count the same
|
|
257
|
+
thing: the conductors of the step's table (pin rows), in links.
|
|
258
|
+
|
|
259
|
+
Providers add their parts: ext-tscircuit the board fabrication and assembly
|
|
260
|
+
order, ext-frame-cad the cut order and its inspection, ext-enclosure-cad the
|
|
261
|
+
print order and the enclosure checks, ext-harness-cad cable preparation,
|
|
262
|
+
terminations and routing checks, ext-docs the spec sheet's designed parts
|
|
263
|
+
and the illustrated assembly guide, ext-drone the flight-controller
|
|
264
|
+
configuration and bench check.
|
|
265
|
+
|
|
266
|
+
## Reproducibility
|
|
267
|
+
|
|
268
|
+
The manifest holds no clock time and no machine path. The release date is
|
|
269
|
+
`SOURCE_DATE_EPOCH`, else the source commit's date. Chrome writes the print
|
|
270
|
+
time and its DOM node counter into each PDF; both are normalised (same
|
|
271
|
+
length, so the cross-reference table holds). Figures are rendered with
|
|
272
|
+
SwiftShader. The fonts the PDFs were printed with are listed with digests,
|
|
273
|
+
since other fonts give other bytes. Rebuilding a version replaces its folder.
|
|
274
|
+
|
|
275
|
+
## Tests
|
|
276
|
+
|
|
277
|
+
`test/release.test.ts` (from the repository root, `npm test`): requirements
|
|
278
|
+
read from a brief, test-id ranges, an earlier package not held against the
|
|
279
|
+
project, unit folding
|
|
280
|
+
and invalid entries; unit BOMs with the provider's BOM merged; ordering
|
|
281
|
+
quantities with spares, packs, MOQ, offers from both offline sources and
|
|
282
|
+
included parts; each precondition refusing (check errors, edited, extra and
|
|
283
|
+
missing outputs, a changed config, an earlier module, no export, uncommitted
|
|
284
|
+
changes); PDF normalisation; and, with Chrome, the package layout, files
|
|
285
|
+
from the standard outputs, the manifest, operations and test ids in
|
|
286
|
+
index.html, a CLI refusal, and two releases with identical bytes. The
|
|
287
|
+
fixture uses core's fake providers. Contributors test their parts with
|
|
288
|
+
`@protoboardai/ext-release/testing` (ext-docs, ext-frame-cad and
|
|
289
|
+
ext-harness-cad on the quadcopter, ext-enclosure-cad on a written build
|
|
290
|
+
report, ext-tscircuit with the IMU tile board, which also exercises the
|
|
291
|
+
release's bring-up checks for a custom board).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "protoboard.eval-case/v0",
|
|
3
|
+
"title": "Cut a release of the smart bottle after a generated file was edited by hand",
|
|
4
|
+
"skills": ["release", "circuit-board", "enclosure-cad"],
|
|
5
|
+
"tags": ["release", "package"],
|
|
6
|
+
"requires": ["chrome", "python-build123d", "tscircuit-node"],
|
|
7
|
+
"fixture": {
|
|
8
|
+
"from": "../../../../examples/smart-bottle",
|
|
9
|
+
"exclude": ["release"],
|
|
10
|
+
"operations": true,
|
|
11
|
+
"steps": [
|
|
12
|
+
{ "note": "the seed: someone swapped the LED driver's MPN in the board's generated BOM", "replace": { "file": "src/boards/bottle-cap/out/bom.csv", "find": "LP5024RSMR", "with": "LP5024RSMT", "count": 1 } }
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"instruction": "Cut release 0.1.1 of the smart bottle with `npx protoboard release --version 0.1.1`: the Manufacturing Package for the cap board, the cap housing and final assembly. The design itself has not changed since 0.1.0 and must not change now: do not edit the UHD model, the parts or the module configs. Bring the project to a releasable state the way the release SOP says, cut the release, then check the package. Tell me what blocked the release and how you cleared it, and the package digest.",
|
|
16
|
+
"tools": { "read": true, "edit": true, "commands": ["npx protoboard *"] },
|
|
17
|
+
"budget": { "turns": 40, "seconds": 1800, "usd": 6 },
|
|
18
|
+
"graders": [
|
|
19
|
+
{
|
|
20
|
+
"id": "package-written",
|
|
21
|
+
"kind": "files",
|
|
22
|
+
"exist": [
|
|
23
|
+
"release/0.1.1/index.html",
|
|
24
|
+
"release/0.1.1/manifest.json",
|
|
25
|
+
"release/0.1.1/00-overview.pdf",
|
|
26
|
+
"release/0.1.1/system/work-order.pdf",
|
|
27
|
+
"release/0.1.1/system/ordering-list.csv",
|
|
28
|
+
"release/0.1.1/units/board/work-order.pdf",
|
|
29
|
+
"release/0.1.1/units/board/test-plan.pdf",
|
|
30
|
+
"release/0.1.1/units/cap/work-order.pdf"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{ "id": "overview-pdf", "kind": "format", "file": "release/0.1.1/00-overview.pdf", "format": "pdf" },
|
|
34
|
+
{ "id": "manifest", "kind": "facts", "file": "release/0.1.1/manifest.json", "facts": [{ "path": "schema", "equals": "protoboard.release/v0" }, { "path": "version", "equals": "0.1.1" }, { "path": "check.errors", "equals": 0 }, { "path": "packageDigest", "matches": "[0-9a-f]{12}" }] },
|
|
35
|
+
{ "id": "bom-as-exported", "kind": "digest", "file": "src/boards/bottle-cap/out/bom.csv", "equals": "sha256:cb00f59c0ab4281cab665f65686972ba1b0ae99f6390a61e60c7e96ba11343b2", "description": "the board's BOM is what circuit:export writes, not the hand edit" },
|
|
36
|
+
{ "id": "package-bom-mpn", "kind": "text", "file": "release/0.1.1/units/board/bom.csv", "matches": ["LP5024RSMR"], "notMatches": ["LP5024RSMT"] },
|
|
37
|
+
{ "id": "blocker-named", "kind": "text", "matches": ["bom\\.csv", "(edit|chang|modif)"], "description": "the answer names the hand-edited BOM" },
|
|
38
|
+
{ "id": "model-untouched", "kind": "digest", "file": "src/boards/bottle-cap/bottle-cap.uhd.ts", "changed": false },
|
|
39
|
+
{ "id": "circuit-config-untouched", "kind": "digest", "file": "src/boards/bottle-cap/circuit.config.ts", "changed": false },
|
|
40
|
+
{ "id": "cad-config-untouched", "kind": "digest", "file": "src/enclosures/bottle-cap/cad.config.ts", "changed": false },
|
|
41
|
+
{ "id": "project-config-untouched", "kind": "digest", "file": "protoboard.config.ts", "changed": false },
|
|
42
|
+
{
|
|
43
|
+
"id": "inspection",
|
|
44
|
+
"kind": "rubric",
|
|
45
|
+
"files": ["release/0.1.1/manifest.json"],
|
|
46
|
+
"rubric": "The agent cut release 0.1.1 of the smart bottle after a hand edit to a generated file blocked it. 1. The answer says what blocked the release (the board's out/bom.csv changed after circuit:export wrote it) and that it was cleared by re-running the generating operation, not by editing the file or the records, and names any further stale output it had to regenerate. 2. The release was not forced (no --allow-dirty, no edited operation records). 3. The answer reports the package digest as the manifest shows it. 4. The answer reports what it inspected in the package (units, documents, warnings carried into the manifest) and states remaining warnings honestly rather than claiming a clean bill of health."
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"solution": [
|
|
50
|
+
{ "protoboard": ["release", "--check", "--version", "0.1.1"], "expect": { "exit": 1 } },
|
|
51
|
+
{ "protoboard": ["run", "circuit:export", "--module", "bottle-cap-board"], "expect": { "status": "succeeded" } },
|
|
52
|
+
{ "note": "the enclosure is stale only when the re-exported board body differs from the one it was designed around; regenerating it is the SOP either way", "protoboard": ["release", "--check", "--version", "0.1.1"] },
|
|
53
|
+
{ "note": "the cap is fitted to the board body: regenerated whether or not the re-export changed it (a re-export that is not reproducible makes it ENCLOSURE_STALE, PB-869)", "protoboard": ["run", "cad:generate", "--module", "bottle-cap-enclosure"], "expect": { "status": "succeeded" } },
|
|
54
|
+
{ "protoboard": ["run", "cad:export", "--module", "bottle-cap-enclosure"], "expect": { "status": "succeeded" } },
|
|
55
|
+
{ "protoboard": ["release", "--check", "--version", "0.1.1"] },
|
|
56
|
+
{ "protoboard": ["release", "--version", "0.1.1"], "expect": { "status": "succeeded" } },
|
|
57
|
+
{ "say": "protoboard check reported PB_OUTPUT_STALE and release --check refused: src/boards/bottle-cap/out/bom.csv was changed after circuit:export wrote it (LP5024RSMR edited to LP5024RSMT). I re-ran circuit:export for bottle-cap-board, which restored the BOM (the same inputs, so the same board.step and board.glb), then regenerated and exported the cap enclosure around the re-exported board (the release SOP: a board export can make its enclosure stale). release --check then passed and release 0.1.1 wrote 3 units, 16 documents; the manifest's packageDigest is in release/0.1.1/manifest.json. The manifest carries 3 warnings: two FW_STRAPPING_PIN on the I2C lines and ENCLOSURE_THIN_WALL." }
|
|
58
|
+
]
|
|
59
|
+
}
|