@osfactory/har 1.2.0 → 1.4.0
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/index.js +1995 -546
- package/dist/templates/agent-skills/factory-line.md +22 -10
- package/dist/templates/agent-skills/har-wt.md +2 -1
- package/dist/templates/agent-skills/skills.manifest.json +25 -9
- package/dist/templates/lines/example-line/.github/workflows/check-manifest.yml +24 -0
- package/dist/templates/lines/example-line/AGENTS.md +48 -0
- package/dist/templates/lines/example-line/README.md +72 -0
- package/dist/templates/lines/example-line/docs/AUTHORING.md +79 -0
- package/dist/templates/lines/example-line/line.json +72 -0
- package/dist/templates/lines/example-line/line.manifest.json +37 -0
- package/dist/templates/lines/example-line/scripts/check-manifest.mjs +103 -0
- package/dist/templates/lines/example-line/stages/example-gate.sh +59 -0
- package/dist/templates/profiles/cli/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/profiles/default/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/profiles/ios/docs/readme/definition-of-done.md +1 -1
- package/dist/templates/shared-docs/readme/session-lifecycle.md +3 -2
- package/package.json +1 -1
|
@@ -4,9 +4,10 @@ Execute **one station** of a declared multi-station program:
|
|
|
4
4
|
|
|
5
5
|
sync → wave plan → parallel HAR slots → cumulative gate → human handoff.
|
|
6
6
|
|
|
7
|
-
The program lives in
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The program lives in an **installed line bundle**, not in this workflow. Find
|
|
8
|
+
it with `har line status` — installed lines live at `.har/lines/<id>/line.json`
|
|
9
|
+
and are recorded in `.har/lines.json`. This workflow is the orchestrator;
|
|
10
|
+
station-specific how-to lives in other skills the program lists.
|
|
10
11
|
|
|
11
12
|
HAR already has the primitives — work units, isolated slots, stages, plugins,
|
|
12
13
|
validation records. A line is composition plus a manifest. Do not invent a
|
|
@@ -19,9 +20,10 @@ This workflow is the same on Claude Code (`/factory-line`), Cursor
|
|
|
19
20
|
|
|
20
21
|
Resolve these before doing anything (ask only if not inferable):
|
|
21
22
|
|
|
22
|
-
1. **Line
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
1. **Line** — run `har line status` (or MCP `har_line_status`). One installed
|
|
24
|
+
line: use it. Several: ask which. **None installed**: stop and offer
|
|
25
|
+
`har line create <id>` or `har line add <spec>` — do not invent GitHub
|
|
26
|
+
issue numbers, a fixture repo, or a program of your own.
|
|
25
27
|
2. **Station id** — default: the first station whose bound work is not all
|
|
26
28
|
done / whose gate has not passed.
|
|
27
29
|
3. **Slot budget** — `har env status` first. One slot per concurrent agent.
|
|
@@ -29,7 +31,7 @@ Resolve these before doing anything (ask only if not inferable):
|
|
|
29
31
|
|
|
30
32
|
## Contract (minimum)
|
|
31
33
|
|
|
32
|
-
A line
|
|
34
|
+
A line program is JSON with `contractVersion: 1`. Required:
|
|
33
35
|
|
|
34
36
|
- `id`, `stations[]` (ordered; each has `id` + `title`)
|
|
35
37
|
- `gate.cumulative` must be `true`
|
|
@@ -56,7 +58,9 @@ does not declare them, do not do them.
|
|
|
56
58
|
|
|
57
59
|
## 0. Load and preflight
|
|
58
60
|
|
|
59
|
-
1.
|
|
61
|
+
1. `har line status [id]` — read the program it names. Confirm
|
|
62
|
+
`gate.cumulative` and `handoff.autonomousShip`. `har line status` already
|
|
63
|
+
reports which stations are green and which is next.
|
|
60
64
|
2. Skills and MCP: confirm required ones are present. Missing optional:
|
|
61
65
|
skip those steps and say so. Do not install third-party packs yourself.
|
|
62
66
|
3. Print: line id, station id, waves, slot plan, and every gate stage whose
|
|
@@ -83,8 +87,16 @@ For each wave, one subagent per group, in parallel. Each:
|
|
|
83
87
|
|
|
84
88
|
## 3. Gate
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
```bash
|
|
91
|
+
har line gate <station> --line <id> --agent <slot>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
That runs every gate stage whose `fromStation` ≤ this station — the cumulative
|
|
95
|
+
set, from data. Add `--force` when the program sets `gate.optInEnv` and you
|
|
96
|
+
want the opt-in jig to run anyway.
|
|
97
|
+
|
|
98
|
+
Line gate stages are **not** part of `har env verify --full`, by design. Run
|
|
99
|
+
both: full verify for the harness contract, `har line gate` for the station.
|
|
88
100
|
|
|
89
101
|
Red gate → fix, re-run. Never hand off a red gate. Never bypass
|
|
90
102
|
(`HAR_SKIP_GATE`, `--no-verify`).
|
|
@@ -57,7 +57,8 @@ Do not substitute ad-hoc test commands for harness verification. Any edit after
|
|
|
57
57
|
## 5. Finish
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
har env complete <id> #
|
|
60
|
+
har env complete <id> # reuse last passing full validation + teardown, keeps the session branch
|
|
61
|
+
har env complete <id> --verify # re-run full verify first if the tree may have changed
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
Report to the user: verification result, the session branch name (so they can push / open a PR), and the slot preview URLs if the app is running.
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
"allowed-tools": "Bash(har *) Bash(npm *) Bash(git *) Bash(docker *) Read Grep Glob Edit Write"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"cursor": {
|
|
17
|
-
|
|
16
|
+
"cursor": {
|
|
17
|
+
"command": true
|
|
18
|
+
},
|
|
19
|
+
"codex": {
|
|
20
|
+
"promptName": "har-setup"
|
|
21
|
+
}
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
"id": "har-wt",
|
|
@@ -26,8 +30,12 @@
|
|
|
26
30
|
"description": "Use BEFORE making any code change in this repository. Launches a har harness slot, which creates an isolated git session worktree — all edits must happen there, never in the main checkout — and verifies the work through the harness when done."
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
|
-
"cursor": {
|
|
30
|
-
|
|
33
|
+
"cursor": {
|
|
34
|
+
"command": true
|
|
35
|
+
},
|
|
36
|
+
"codex": {
|
|
37
|
+
"promptName": "har-wt"
|
|
38
|
+
}
|
|
31
39
|
},
|
|
32
40
|
{
|
|
33
41
|
"id": "har-maintain",
|
|
@@ -41,8 +49,12 @@
|
|
|
41
49
|
"allowed-tools": "Bash(har *) Bash(git *) Read Grep Glob Edit Write"
|
|
42
50
|
}
|
|
43
51
|
},
|
|
44
|
-
"cursor": {
|
|
45
|
-
|
|
52
|
+
"cursor": {
|
|
53
|
+
"command": true
|
|
54
|
+
},
|
|
55
|
+
"codex": {
|
|
56
|
+
"promptName": "har-maintain"
|
|
57
|
+
}
|
|
46
58
|
},
|
|
47
59
|
{
|
|
48
60
|
"id": "factory-line",
|
|
@@ -51,11 +63,15 @@
|
|
|
51
63
|
"claude": {
|
|
52
64
|
"frontmatter": {
|
|
53
65
|
"name": "factory-line",
|
|
54
|
-
"description": "Execute one station of
|
|
66
|
+
"description": "Execute one station of an installed factory line (har line status / .har/lines/<id>/line.json): parallel HAR slots, cumulative gate via har line gate, human handoff. Use when asked to run a factory line, the next station, or a milestone program."
|
|
55
67
|
}
|
|
56
68
|
},
|
|
57
|
-
"cursor": {
|
|
58
|
-
|
|
69
|
+
"cursor": {
|
|
70
|
+
"command": true
|
|
71
|
+
},
|
|
72
|
+
"codex": {
|
|
73
|
+
"promptName": "har-factory-line"
|
|
74
|
+
}
|
|
59
75
|
}
|
|
60
76
|
]
|
|
61
77
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Check line bundle
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check-manifest:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: 20
|
|
16
|
+
- name: Validate line manifest and program
|
|
17
|
+
run: node scripts/check-manifest.mjs
|
|
18
|
+
- name: Stage scripts are executable
|
|
19
|
+
run: |
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
for script in stages/*.sh; do
|
|
22
|
+
[ -x "$script" ] || { echo "not executable: $script" >&2; exit 1; }
|
|
23
|
+
bash -n "$script"
|
|
24
|
+
done
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Authoring guide for coding agents
|
|
2
|
+
|
|
3
|
+
This repository is a **HAR factory line bundle**. Read this before changing it.
|
|
4
|
+
|
|
5
|
+
## Is this a line or a verification plugin?
|
|
6
|
+
|
|
7
|
+
Ask one question: **should this run on every `har env verify --full`?**
|
|
8
|
+
|
|
9
|
+
| Answer | Bundle kind | Template | Install |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| Yes — it must gate every change | Verification plugin | [har-plugin](https://github.com/os-factory/har-plugin) | `har env add-plugin <spec>` |
|
|
12
|
+
| No — it gates a *station* of a program | Factory line | this repo | `har line add <spec>` |
|
|
13
|
+
|
|
14
|
+
Typical lines: a migration executed in waves, an onboarding program, a
|
|
15
|
+
heavy proof jig (Docker lab, fixture end-to-end, device farm) that must exist
|
|
16
|
+
and be runnable but must not be on the hot path of every verify.
|
|
17
|
+
|
|
18
|
+
If you find yourself wanting to add a line stage to `verificationStages`, stop:
|
|
19
|
+
you wanted a plugin.
|
|
20
|
+
|
|
21
|
+
## Hard rules
|
|
22
|
+
|
|
23
|
+
- `line.manifest.json` declares `"kind": "line"` and **must not** declare
|
|
24
|
+
`verificationStages`. The schema rejects it.
|
|
25
|
+
- `gate.cumulative` is `true`. A stage tagged `fromStation: X` is required at X
|
|
26
|
+
**and every later station**. Adding a station may never drop an earlier
|
|
27
|
+
station's stages.
|
|
28
|
+
- `handoff.autonomousShip` is `false`. Agents hand off; humans merge and
|
|
29
|
+
release.
|
|
30
|
+
- `skills[]` and `mcp[]` are **declarations with install hints**. HAR does not
|
|
31
|
+
vendor skill packs or MCP servers, and a line never introduces a core tool
|
|
32
|
+
named after a tracker or a framework.
|
|
33
|
+
- Station names, tracker ids, branch strategy, and fixture repos are *instance
|
|
34
|
+
data*. They belong in `line.json`, never in the tooling.
|
|
35
|
+
|
|
36
|
+
## Validate
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node scripts/check-manifest.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then prove the invariant on a scratch harness:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
har line add ./ # from a repo with a .har/ harness
|
|
46
|
+
har line status <id>
|
|
47
|
+
git diff -- .har/stages.json # verificationStages must be untouched
|
|
48
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# har-line
|
|
2
|
+
|
|
3
|
+
A GitHub template for authoring **HAR factory lines**.
|
|
4
|
+
|
|
5
|
+
A *factory line* is a program: an ordered set of **stations** plus a
|
|
6
|
+
**cumulative gate**. It composes what HAR 1.0 already has — work units, slots,
|
|
7
|
+
stages, plugins, skills, MCP. It is not a fourth marketplace and not a second
|
|
8
|
+
stage runner.
|
|
9
|
+
|
|
10
|
+
> **A line is not a verification plugin.** Installing a line registers its
|
|
11
|
+
> stages but **never** adds them to `verificationStages`. Default
|
|
12
|
+
> `har env verify --full` takes exactly as long as it did before. That is the
|
|
13
|
+
> entire reason the two bundle kinds are separate.
|
|
14
|
+
>
|
|
15
|
+
> If your check should gate *every* verify, use
|
|
16
|
+
> [os-factory/har-plugin](https://github.com/os-factory/har-plugin) instead.
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
1. **Use this template** → name your repo (e.g. `onboarding-line`).
|
|
21
|
+
2. Rename the example: `line.manifest.json` `id`, `line.json` `id`/`title`, and
|
|
22
|
+
the stage script under `stages/`.
|
|
23
|
+
3. Describe your stations in `line.json`.
|
|
24
|
+
4. Install it into a product repo:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
har line add github:your-org/onboarding-line
|
|
28
|
+
har line status onboarding-line
|
|
29
|
+
har line gate S2 --line onboarding-line
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Package layout
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
har-line/
|
|
36
|
+
├── line.manifest.json # kind: line — what `har line add` applies
|
|
37
|
+
├── line.json # the program: stations, gate, handoff
|
|
38
|
+
├── stages/
|
|
39
|
+
│ └── example-gate.sh # extra stage — registered, NOT on verify
|
|
40
|
+
├── docs/AUTHORING.md # how to write a line
|
|
41
|
+
├── scripts/check-manifest.mjs
|
|
42
|
+
└── AGENTS.md # line vs verification plugin, for coding agents
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Install channels
|
|
46
|
+
|
|
47
|
+
| Spec | Example |
|
|
48
|
+
|---|---|
|
|
49
|
+
| Local path | `har line add ./har-line` |
|
|
50
|
+
| Git | `har line add github:acme/onboarding-line` |
|
|
51
|
+
| npm | `har line add @acme/onboarding-line` |
|
|
52
|
+
|
|
53
|
+
Add a `package.json` (name, version, `files`) only if you want the npm channel —
|
|
54
|
+
git and path installs need nothing else.
|
|
55
|
+
|
|
56
|
+
Resolution order is path → git → bundled id → npm, the same resolver plugins
|
|
57
|
+
use. The package root must contain `line.manifest.json`.
|
|
58
|
+
|
|
59
|
+
`har env add-plugin` **refuses** a line bundle and points at `har line add`.
|
|
60
|
+
|
|
61
|
+
## The invariant
|
|
62
|
+
|
|
63
|
+
After `har line add`, `.har/stages.json` `verificationStages` is unchanged —
|
|
64
|
+
no new ids, same default verify duration. `scripts/check-manifest.mjs` asserts
|
|
65
|
+
the manifest side of that; the CLI asserts the applied side and refuses to
|
|
66
|
+
write if the verify plan moved.
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- [os-factory/har](https://github.com/os-factory/har) — the CLI and MCP control plane
|
|
71
|
+
- [os-factory/har-plugin](https://github.com/os-factory/har-plugin) — template for **verification plugins** (these *do* join verify)
|
|
72
|
+
- [Factory lines guide](https://har.osfactory.dev/docs/guides/factory-lines/)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Authoring a factory line
|
|
2
|
+
|
|
3
|
+
## 1. The program (`line.json`)
|
|
4
|
+
|
|
5
|
+
`contractVersion: 1`. Additive fields are fine; renaming a field is a new
|
|
6
|
+
version.
|
|
7
|
+
|
|
8
|
+
| Field | Required | Meaning |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `id` | yes | Stable kebab id. Must match `line.manifest.json`. |
|
|
11
|
+
| `title` | yes | Human title. |
|
|
12
|
+
| `stations[]` | yes | Ordered. Array order *is* station order. |
|
|
13
|
+
| `stations[].id` | yes | Stable, shell-friendly. |
|
|
14
|
+
| `stations[].work` | no | Tracker binding: `source` (`github`, `linear`, `none`, …), `ids[]` passed to `har env launch --work-id`. |
|
|
15
|
+
| `stations[].waves` | no | Each inner array is one wave; cells in a wave run in parallel slots. |
|
|
16
|
+
| `skills[]` | no | Skills the line expects. `role` is `orchestrator` or `station`; `install` is a hint (`repo:…`, `upstream:url`, `agent-managed`). |
|
|
17
|
+
| `mcp[]` | no | MCP **servers** the line expects. `required: false` means skip those steps when absent. |
|
|
18
|
+
| `plugins[]` | no | Verification plugin ids the gate assumes (`har env add-plugin`). |
|
|
19
|
+
| `gate.cumulative` | yes | Must be `true`. |
|
|
20
|
+
| `gate.optInEnv` | no | When set (e.g. `HAR_FIXTURE_E2E`), the gate is skipped unless that env is `1`. |
|
|
21
|
+
| `gate.stages[].fromStation` | yes | The ratchet tag: required from this station onward. |
|
|
22
|
+
| `extraStages[]` | no | Stages this line adds beyond the profile defaults. |
|
|
23
|
+
| `handoff.autonomousShip` | yes | Must be `false`. |
|
|
24
|
+
| `traveler` | no | Where durable notes live given the repo's merge policy. |
|
|
25
|
+
| `prototypeNotes[]` | no | Instance tactics that must not leak into the primitive. |
|
|
26
|
+
|
|
27
|
+
## 2. The manifest (`line.manifest.json`)
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"kind": "line",
|
|
32
|
+
"id": "your-line",
|
|
33
|
+
"program": "line.json",
|
|
34
|
+
"files": [
|
|
35
|
+
{ "src": "stages/your-gate.sh", "dest": ".har/stages/your-gate.sh", "executable": true }
|
|
36
|
+
],
|
|
37
|
+
"stages": [{ "id": "your-gate", "kind": "test", "script": "stages/your-gate.sh", "tier": "full" }]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `files[]` are copied into the target repo verbatim.
|
|
42
|
+
- `stages[]` are **registered** in `.har/stages.json` — and nothing else. They
|
|
43
|
+
are not appended to `verificationStages`, and the CLI refuses to write if
|
|
44
|
+
applying the bundle would have moved that list.
|
|
45
|
+
- Declaring `verificationStages` here is a schema error, not a warning.
|
|
46
|
+
|
|
47
|
+
## 3. The gate stage
|
|
48
|
+
|
|
49
|
+
A line stage is an ordinary HAR stage script: stdout is one JSON result object,
|
|
50
|
+
stderr is progress, `$1` is the agent slot id, artifacts go under
|
|
51
|
+
`.har/artifacts/<stage-id>/`. See `.har/STAGES.md` in any harness for the full
|
|
52
|
+
contract.
|
|
53
|
+
|
|
54
|
+
Run it through the line, not through verify:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
har line gate S2 --line your-line
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 4. Growing the ratchet
|
|
61
|
+
|
|
62
|
+
To ask a question the previous gate forgot:
|
|
63
|
+
|
|
64
|
+
1. Add a stage (or a `doctor` check) that asks it.
|
|
65
|
+
2. Tag it `fromStation` of the station that made the question askable.
|
|
66
|
+
3. Leave every earlier tag in place.
|
|
67
|
+
|
|
68
|
+
That is how a review comment becomes a permanent question instead of a habit.
|
|
69
|
+
|
|
70
|
+
## 5. Publish
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git init && git commit -am "feat: my line"
|
|
74
|
+
gh repo create your-org/your-line --public --source . --push
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Consumers install with `har line add github:your-org/your-line`. For npm, keep
|
|
78
|
+
`package.json` and `npm publish` — `har line add @your-org/your-line` packs it
|
|
79
|
+
with `npm pack` and reads `line.manifest.json` from the package root.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contractVersion": 1,
|
|
3
|
+
"id": "example-line",
|
|
4
|
+
"title": "Example factory line",
|
|
5
|
+
"description": "A three-station program showing the shape a factory line takes: stations that produce something, a cumulative gate that grows as the line grows, and a handoff that a human owns. Rename it and make the stations describe your own work.",
|
|
6
|
+
"skills": [
|
|
7
|
+
{
|
|
8
|
+
"id": "factory-line",
|
|
9
|
+
"role": "orchestrator",
|
|
10
|
+
"install": "repo:.claude/skills/factory-line"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"mcp": [
|
|
14
|
+
{
|
|
15
|
+
"name": "github",
|
|
16
|
+
"why": "issue tracker and pull requests",
|
|
17
|
+
"required": false
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"plugins": [],
|
|
21
|
+
"stations": [
|
|
22
|
+
{
|
|
23
|
+
"id": "S1",
|
|
24
|
+
"title": "Draft",
|
|
25
|
+
"description": "Agree the contract before anyone writes code: schema, interfaces, the invariant that must hold.",
|
|
26
|
+
"work": { "source": "none", "ids": [], "optional": true }
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "S2",
|
|
30
|
+
"title": "Build",
|
|
31
|
+
"description": "Implement the contract in isolated slots, one work unit per slot.",
|
|
32
|
+
"work": { "source": "none", "ids": [], "optional": true }
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "S3",
|
|
36
|
+
"title": "Prove",
|
|
37
|
+
"description": "Demonstrate the invariant on a real repository, then hand off for human review.",
|
|
38
|
+
"work": { "source": "none", "ids": [], "optional": true }
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"gate": {
|
|
42
|
+
"cumulative": true,
|
|
43
|
+
"optInEnv": null,
|
|
44
|
+
"stages": [
|
|
45
|
+
{
|
|
46
|
+
"id": "example-gate",
|
|
47
|
+
"fromStation": "S2",
|
|
48
|
+
"tier": "full"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"extraStages": [
|
|
53
|
+
{
|
|
54
|
+
"id": "example-gate",
|
|
55
|
+
"kind": "test",
|
|
56
|
+
"tier": "full",
|
|
57
|
+
"description": "Line-owned gate — registered in .har/stages.json, absent from verificationStages"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"handoff": {
|
|
61
|
+
"autonomousShip": false,
|
|
62
|
+
"waitFor": "human review before merge or release"
|
|
63
|
+
},
|
|
64
|
+
"traveler": {
|
|
65
|
+
"kind": "github-issue-comment",
|
|
66
|
+
"note": "Durable notes live on the tracker issue — a squash merge drops commit-body footers."
|
|
67
|
+
},
|
|
68
|
+
"prototypeNotes": [
|
|
69
|
+
"Station names (Draft/Build/Prove) are instance data — rename them for your program.",
|
|
70
|
+
"This example binds no tracker work; set stations[].work.source and .ids to bind GitHub or Linear."
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "line",
|
|
3
|
+
"id": "example-line",
|
|
4
|
+
"title": "Example factory line",
|
|
5
|
+
"program": "line.json",
|
|
6
|
+
"files": [
|
|
7
|
+
{
|
|
8
|
+
"src": "stages/example-gate.sh",
|
|
9
|
+
"dest": ".har/stages/example-gate.sh",
|
|
10
|
+
"executable": true
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"stages": [
|
|
14
|
+
{
|
|
15
|
+
"id": "example-gate",
|
|
16
|
+
"kind": "test",
|
|
17
|
+
"description": "Line-owned gate for example-line — registered, never on verify",
|
|
18
|
+
"script": "stages/example-gate.sh",
|
|
19
|
+
"requiresAgentId": true,
|
|
20
|
+
"artifacts": [
|
|
21
|
+
{
|
|
22
|
+
"path": ".har/artifacts/example-gate",
|
|
23
|
+
"kind": "directory",
|
|
24
|
+
"description": "Artifacts for the example-gate stage"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"tier": "full"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"nextSteps": [
|
|
31
|
+
"Edit .har/lines/example-line/line.json — rename the line and describe your stations",
|
|
32
|
+
"Edit .har/stages/example-gate.sh — replace the TODO with the real gate",
|
|
33
|
+
"har line status example-line",
|
|
34
|
+
"har line gate S2 --line example-line"
|
|
35
|
+
],
|
|
36
|
+
"docsPath": ".har/lines/example-line/README.md"
|
|
37
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Validate this line bundle without installing it.
|
|
4
|
+
*
|
|
5
|
+
* Checks the invariants that make a line a line — most importantly that the
|
|
6
|
+
* manifest declares no `verificationStages`, so applying it can never widen
|
|
7
|
+
* `har env verify --full`.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node scripts/check-manifest.mjs [bundle-dir]
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
12
|
+
import { join, resolve, dirname } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const bundleDir = resolve(process.argv[2] ?? join(dirname(fileURLToPath(import.meta.url)), '..'));
|
|
16
|
+
const errors = [];
|
|
17
|
+
const warnings = [];
|
|
18
|
+
|
|
19
|
+
function readJson(relPath) {
|
|
20
|
+
const abs = join(bundleDir, relPath);
|
|
21
|
+
if (!existsSync(abs)) {
|
|
22
|
+
errors.push(`missing file: ${relPath}`);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(abs, 'utf8'));
|
|
27
|
+
} catch (err) {
|
|
28
|
+
errors.push(`invalid JSON in ${relPath}: ${err.message}`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const manifest = readJson('line.manifest.json');
|
|
34
|
+
|
|
35
|
+
if (manifest) {
|
|
36
|
+
if (manifest.kind !== 'line') {
|
|
37
|
+
errors.push('line.manifest.json must declare "kind": "line" (a plugin manifest is not a line)');
|
|
38
|
+
}
|
|
39
|
+
if (manifest.verificationStages !== undefined) {
|
|
40
|
+
errors.push(
|
|
41
|
+
'line.manifest.json must NOT declare verificationStages — line gate stages are opt-in and run via `har line gate`',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (typeof manifest.id !== 'string' || !/^[a-z0-9][a-z0-9._-]*$/.test(manifest.id)) {
|
|
45
|
+
errors.push('line.manifest.json id must be a lowercase slug');
|
|
46
|
+
}
|
|
47
|
+
for (const file of manifest.files ?? []) {
|
|
48
|
+
if (!existsSync(join(bundleDir, file.src))) {
|
|
49
|
+
errors.push(`files[].src not found: ${file.src}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const program = manifest ? readJson(manifest.program ?? 'line.json') : null;
|
|
55
|
+
|
|
56
|
+
if (manifest && program) {
|
|
57
|
+
if (program.id !== manifest.id) {
|
|
58
|
+
errors.push(`program id "${program.id}" does not match manifest id "${manifest.id}"`);
|
|
59
|
+
}
|
|
60
|
+
if (program.contractVersion !== 1) {
|
|
61
|
+
errors.push('program contractVersion must be 1');
|
|
62
|
+
}
|
|
63
|
+
if (program.gate?.cumulative !== true) {
|
|
64
|
+
errors.push('gate.cumulative must be true — a line never drops an earlier station\'s stages');
|
|
65
|
+
}
|
|
66
|
+
if (program.handoff?.autonomousShip !== false) {
|
|
67
|
+
errors.push('handoff.autonomousShip must be false — agents hand off, they do not ship');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const stationIds = (program.stations ?? []).map((s) => s.id);
|
|
71
|
+
if (stationIds.length === 0) {
|
|
72
|
+
errors.push('program needs at least one station');
|
|
73
|
+
}
|
|
74
|
+
if (new Set(stationIds).size !== stationIds.length) {
|
|
75
|
+
errors.push('station ids must be unique');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const registeredIds = new Set((manifest.stages ?? []).map((s) => s.id));
|
|
79
|
+
for (const stage of program.gate?.stages ?? []) {
|
|
80
|
+
if (!stationIds.includes(stage.fromStation)) {
|
|
81
|
+
errors.push(`gate stage "${stage.id}" tags unknown station "${stage.fromStation}"`);
|
|
82
|
+
}
|
|
83
|
+
if (!registeredIds.has(stage.id)) {
|
|
84
|
+
warnings.push(
|
|
85
|
+
`gate stage "${stage.id}" is not registered by this bundle — the target repo must already provide it (e.g. via a verification plugin)`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const warning of warnings) {
|
|
92
|
+
console.warn(`warn: ${warning}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (errors.length > 0) {
|
|
96
|
+
for (const err of errors) {
|
|
97
|
+
console.error(`error: ${err}`);
|
|
98
|
+
}
|
|
99
|
+
console.error(`\n${errors.length} error(s) in ${bundleDir}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`ok: ${bundleDir} is a valid HAR line bundle`);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Line-owned gate for example-line (never on verify)
|
|
3
|
+
# Custom HAR stage: example-gate (kind: test)
|
|
4
|
+
#
|
|
5
|
+
# Stage script contract (full reference: .har/STAGES.md):
|
|
6
|
+
# - stdout: a single JSON result object (status, stageId, agent_id, ...)
|
|
7
|
+
# - stderr: human-readable progress
|
|
8
|
+
# - $1: agent slot id; extra args may follow
|
|
9
|
+
# - artifacts: write reports/screenshots/logs under .har/artifacts/example-gate/
|
|
10
|
+
# - exit code: the real status (0 = pass)
|
|
11
|
+
#
|
|
12
|
+
# Usage: ./.har/stages/example-gate.sh <agent-id> [extra args...]
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# 1.0 stage surface: the runner exports WORK_DIR, ENV_FILE, AGENT_ID and
|
|
16
|
+
# HAR_HARNESS_DIR, with harness.env and the slot env file already sourced —
|
|
17
|
+
# agent-slot.sh is retired (1.0 migration).
|
|
18
|
+
HARNESS_DIR="${HAR_HARNESS_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
19
|
+
REPO_ROOT="$(cd "$HARNESS_DIR/.." && pwd)"
|
|
20
|
+
|
|
21
|
+
AGENT_ID="${1:-${AGENT_ID:?Usage: example-gate.sh <agent-id> [extra args...]}}"
|
|
22
|
+
now_ms() { node -e 'process.stdout.write(String(Date.now()))' 2>/dev/null || echo 0; }
|
|
23
|
+
|
|
24
|
+
# JSON-escape step output; truncate to 50 lines in node (avoids SIGPIPE under pipefail).
|
|
25
|
+
escape_step_output() {
|
|
26
|
+
printf '%s' "$1" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const s=d.trim().split('\n').slice(0,50).join('\n');process.stdout.write(JSON.stringify(s))})" 2>/dev/null || echo '""'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ENV_FILE="${ENV_FILE:?No slot env for agent ${AGENT_ID} — run har env launch ${AGENT_ID} first}"
|
|
30
|
+
WORK_DIR="${WORK_DIR:?No slot work dir for agent ${AGENT_ID} — run har env launch ${AGENT_ID} first}"
|
|
31
|
+
ARTIFACTS_DIR="$HARNESS_DIR/artifacts/example-gate"
|
|
32
|
+
mkdir -p "$ARTIFACTS_DIR"
|
|
33
|
+
|
|
34
|
+
echo "==> [example-gate agent-${AGENT_ID}] running in ${WORK_DIR}" >&2
|
|
35
|
+
START=$(now_ms)
|
|
36
|
+
|
|
37
|
+
# ── TODO: implement the stage ────────────────────────────────────────────────
|
|
38
|
+
# Run your checks from $WORK_DIR (the agent's isolated checkout); the slot's
|
|
39
|
+
# ports and env are loaded from $ENV_FILE. Save artifacts to $ARTIFACTS_DIR.
|
|
40
|
+
set +e
|
|
41
|
+
OUTPUT=$(cd "$WORK_DIR" && echo "TODO: implement .har/stages/example-gate.sh" && false)
|
|
42
|
+
EXIT_CODE=$?
|
|
43
|
+
set -e
|
|
44
|
+
|
|
45
|
+
END=$(now_ms)
|
|
46
|
+
STATUS="fail"
|
|
47
|
+
[ "$EXIT_CODE" = "0" ] && STATUS="pass"
|
|
48
|
+
OUTPUT_JSON=$(escape_step_output "$OUTPUT")
|
|
49
|
+
|
|
50
|
+
node -e "process.stdout.write(JSON.stringify({
|
|
51
|
+
status: '$STATUS',
|
|
52
|
+
stageId: 'example-gate',
|
|
53
|
+
kind: 'test',
|
|
54
|
+
agent_id: $AGENT_ID,
|
|
55
|
+
total_ms: $(( END - START )),
|
|
56
|
+
output: $OUTPUT_JSON
|
|
57
|
+
}, null, 2) + '\n');"
|
|
58
|
+
|
|
59
|
+
exit "$EXIT_CODE"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- [ ] New behavior has automated test coverage
|
|
7
7
|
- [ ] Changes committed **in the session worktree** with a clear message
|
|
8
8
|
- [ ] Present session handoff (summary, branch, preview URLs) and **wait for user** before `complete`, push, or PR
|
|
9
|
-
- [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) —
|
|
9
|
+
- [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — reuse last passing full validation + teardown, branch kept. Pass `--verify` / `verify: true` if the tree may have changed.
|
|
10
10
|
|
|
11
11
|
### Session handoff
|
|
12
12
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- [ ] Changes committed **in the session worktree** with a clear message
|
|
8
8
|
- [ ] The user got the preview URLs to test the app themselves
|
|
9
9
|
- [ ] Present session handoff (summary, branch, preview URLs) and **wait for user** before `complete`, push, or PR
|
|
10
|
-
- [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) —
|
|
10
|
+
- [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — reuse last passing full validation + teardown, branch kept. Pass `--verify` / `verify: true` if the tree may have changed.
|
|
11
11
|
|
|
12
12
|
### Session handoff
|
|
13
13
|
|