@manhquy/flow-skill 0.1.0-rc.2 → 0.1.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/CHANGELOG.md +67 -4
- package/README.md +15 -5
- package/README_VN.md +9 -2
- package/SECURITY.md +1 -1
- package/bin/cli.mjs +4 -1
- package/package.json +1 -1
- package/skills/flow/SKILL.md +2 -2
- package/skills/flow/harness/GAP-MATRIX-0.1.17.md +41 -0
- package/skills/flow/harness/README.md +40 -6
- package/skills/flow/harness/flow_harness.py +71 -2
- package/skills/flow/harness/pins/harness-cli-v0.1.17.sha256sums +9 -0
- package/skills/flow/references/agent-stage-mapping.md +1 -1
- package/skills/flow/references/auto-run.md +1 -1
- package/skills/flow/runner/flow.sh +77 -8
- package/skills-manifest.json +4 -2
- package/src/constants.mjs +17 -2
- package/src/help.mjs +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@manhquy/flow-skill`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
-
## [0.1.0
|
|
5
|
+
## [0.1.0] — 2026-07-19 — GA (first stable, `latest`)
|
|
6
|
+
|
|
7
|
+
First stable release of the installer. Graduates the rc.1→rc.3 line after two live OIDC
|
|
8
|
+
publishes + independent verification; publishes to dist-tag **`latest`** so bare
|
|
9
|
+
`npx @manhquy/flow-skill` / `npm i` resolves it (no manual dist-tag promotion needed).
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Ships skill product **v0.24.0** (harness trust-align — honest `proof_source`, STRICT durable
|
|
13
|
+
modes, canonical harness skill; see root `CHANGELOG.md` `## 0.24.0`). rc.1–rc.3 shipped v0.22.0.
|
|
14
|
+
- No installer code change vs rc.3 — same CLI, detection, lock, and five install targets
|
|
15
|
+
(Claude, Codex, Antigravity, Cursor, universal `~/.agents/skills`).
|
|
16
|
+
|
|
17
|
+
## [0.1.0-rc.3] — 2026-07-18
|
|
18
|
+
|
|
19
|
+
Ships the same skill product **v0.22.0** as rc.2, plus pipeline/UX fixes found after the first
|
|
20
|
+
OIDC publish + independent verification.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Nightly registry health false-red:** `scripts/smoke.mjs` ran `npx` from inside
|
|
24
|
+
`npm-wrapper/`, so npx resolved the **local workspace package** instead of the registry
|
|
25
|
+
(`flow-skill: not found`) even when the published package was healthy. Smoke now uses an
|
|
26
|
+
empty temp cwd + `npx --package=… flow-skill`; nightly runs from `$RUNNER_TEMP`.
|
|
27
|
+
- **CI bash-suite Windows cancelled at 15m:** full 33-suite run needs ~20–25m on Windows Git
|
|
28
|
+
Bash (measured run `29630992330` — mid-`test_flow_eval` when cut). `timeout-minutes` is now
|
|
29
|
+
**30 on windows-latest**, 15 elsewhere. `tests/run_all.sh` prints per-suite `wall_s`.
|
|
30
|
+
- **`promote_to`:** early-exit before install/test (no full job burn); input description states
|
|
31
|
+
it is **manual-only** (OIDC cannot `npm dist-tag add` — E401 on run `29556075144`).
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Dual-version UX: `--help` shows `flow-skill v<pkg> (ships skill v<skill>)`; JSONL `plan`
|
|
35
|
+
event gains additive `skillVersion`. Unit tests pin values from `package.json` + `SKILL.md`.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- `bin/cli.mjs` git mode `100755` (executable bit in tarball).
|
|
39
|
+
|
|
40
|
+
## [0.1.0-rc.2] — 2026-07-17 — LIVE (published 2026-07-17, `latest` + `rc` both point here)
|
|
6
41
|
|
|
7
42
|
Ships `skills/flow` v0.22.0 (concierge front-door + standalone native rituals — see the root
|
|
8
43
|
repo's CHANGELOG.md `## 0.22.0` entry for the full skill-content changelog). This wrapper
|
|
@@ -68,10 +103,38 @@ two hardening items found while verifying it all.
|
|
|
68
103
|
both `install.sh` and `install.ps1` carry the Antigravity restart/reload hint, and the old
|
|
69
104
|
hardcoded Claude+Codex-only Done line is gone from both.
|
|
70
105
|
|
|
71
|
-
###
|
|
106
|
+
### Fixed — CI publish guard (found on the actual first real run of this workflow)
|
|
107
|
+
- `publish-npm-wrapper.yml`'s `Guard — assert no NODE_AUTH_TOKEN present (OIDC only)` step
|
|
108
|
+
**hard-failed every run, including this release's first attempt** (run `29554226397`,
|
|
109
|
+
2026-07-17). Root-caused via a temporary diagnostic step (added, run 3×, removed same
|
|
110
|
+
session): `NODE_AUTH_TOKEN` is unset right after `actions/checkout@v4` but becomes a real
|
|
111
|
+
short-lived OIDC-exchanged value the instant `actions/setup-node@v4` runs with
|
|
112
|
+
`registry-url` set — that's the current `actions/setup-node@v4` (a moving tag) doing its own
|
|
113
|
+
native npm OIDC handling, not a leaked static secret. The guard's 2026-07-12 design assumed
|
|
114
|
+
the older model where `npm publish` alone performed the OIDC exchange with nothing set
|
|
115
|
+
beforehand; it never matched real `actions/setup-node@v4` behavior and had a 0% pass rate
|
|
116
|
+
since the day it was written. **Removed** — see the workflow file's inline comment for the
|
|
117
|
+
full evidence chain.
|
|
118
|
+
- **npm Trusted Publisher was never actually bound** on npmjs.com for this package (confirmed:
|
|
119
|
+
first real publish attempt after the guard fix still 404'd — `PUT
|
|
120
|
+
.../@manhquy%2fflow-skill — Not found`). The 2026-07-12 runbook flagged this config as `TBD`
|
|
121
|
+
and it was never closed out. Configured now: `owner=manhquydev`, `repo=flow-skill`,
|
|
122
|
+
`workflow=publish-npm-wrapper.yml`, `environment=npm-publish`. `npm publish --provenance`
|
|
123
|
+
now succeeds via pure OIDC — confirmed live, SLSA attestation present.
|
|
124
|
+
- **`npm dist-tag add` does NOT work via OIDC** (confirmed live: `E401 Unable to authenticate`
|
|
125
|
+
even immediately after a successful OIDC-authenticated publish in the same job). npm has not
|
|
126
|
+
extended Trusted Publishing to this endpoint yet (matches
|
|
127
|
+
`plans/reports/researcher-260712-1006-npm-trusted-publisher-first-publish-edge-cases-report.md`
|
|
128
|
+
item #12, "OIDC support in development", no ship date). Promoting `0.1.0-rc.2` to `latest`
|
|
129
|
+
required the same manual Granular-Access-Token-with-Bypass-2FA flow used for the rc.1
|
|
130
|
+
bootstrap publish — see `RELEASE_CHECKLIST.md` for the exact steps and why this account
|
|
131
|
+
(created 2026-07-11, after npm's Sept-2025 TOTP-enrollment cutoff) has no other CLI-auth path
|
|
132
|
+
for write operations.
|
|
133
|
+
|
|
134
|
+
### Release command (already run for this release)
|
|
72
135
|
```
|
|
73
|
-
cd npm-wrapper && npm test #
|
|
74
|
-
git tag npm@0.1.0-rc.2 && git push
|
|
136
|
+
cd npm-wrapper && npm test # confirmed green first
|
|
137
|
+
git tag npm@0.1.0-rc.2 && git push origin npm@0.1.0-rc.2 # triggered the workflow (after the guard fix + TP config above)
|
|
75
138
|
```
|
|
76
139
|
|
|
77
140
|
## [0.1.0-rc.1] — 2026-07-12
|
package/README.md
CHANGED
|
@@ -10,13 +10,21 @@ One-command installer that copies the [flow](https://github.com/manhquydev/flow-
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
12
|
```
|
|
13
|
-
# Pre-release channel (current — v0.1.0-rc.
|
|
13
|
+
# Pre-release channel (current — v0.1.0-rc.3, ships skill v0.22.0)
|
|
14
14
|
npx @manhquy/flow-skill@rc
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
An interactive prompt asks which agents to install to. Pick one or more, confirm, done.
|
|
18
18
|
|
|
19
|
-
> **RC phase**: pin `@rc` (dist-tag) or a specific version like `@0.1.0-rc.
|
|
19
|
+
> **RC phase**: pin `@rc` (dist-tag) or a specific version like `@0.1.0-rc.3`. `npx @manhquy/flow-skill@0.1.x` will start working after stable `0.1.0` is published — semver ranges do **not** match pre-release tuples by default. See [SECURITY.md](./SECURITY.md).
|
|
20
|
+
>
|
|
21
|
+
> **`npm i` alone is not enough.** `npm install @manhquy/flow-skill` only adds the installer
|
|
22
|
+
> package to `node_modules`. You must **run** it (`npx @manhquy/flow-skill@rc` or
|
|
23
|
+
> `npx flow-skill` after install) to copy the skill into agent homes.
|
|
24
|
+
>
|
|
25
|
+
> **Two version axes:** package `version` in this folder is the **installer** (e.g. `0.1.0-rc.3`).
|
|
26
|
+
> Skill product version lives in `skills/flow/SKILL.md` (`metadata.version`, e.g. `0.22.0`).
|
|
27
|
+
> `--help` and the JSONL `plan` event expose both (`version` + `skillVersion`).
|
|
20
28
|
|
|
21
29
|
## Non-interactive
|
|
22
30
|
|
|
@@ -58,7 +66,7 @@ line tells you exactly what to do for whichever targets you installed:
|
|
|
58
66
|
- Claude Code: type `/flow`.
|
|
59
67
|
- Codex CLI: type `$flow` (restart Codex once to load a new skill).
|
|
60
68
|
- Antigravity: restart/reload the IDE (or restart `agy`) to load the new skill, then type `/flow`.
|
|
61
|
-
- Agents home: restart/reload your tool if it does not auto-detect new skills.
|
|
69
|
+
- Agents home (`~/.agents/skills/`): restart/reload your tool if it does not auto-detect new skills.
|
|
62
70
|
- Cursor: **install verified, live runner execution not yet independently confirmed** — Cursor
|
|
63
71
|
has no headless CLI probe available for automated verification (unlike Antigravity's `agy -p`
|
|
64
72
|
or Codex's `codex exec`); restart/reload Cursor after install and check the Agent panel for
|
|
@@ -86,6 +94,8 @@ rm -rf <project>/.claude/skills/flow
|
|
|
86
94
|
- **Windows `EBUSY` / `EPERM` mid-install**: an agent (Claude Code, Codex, Antigravity IDE) is holding a file inside the destination. Close the agent and re-run. The installer already retries with 100/300/900 ms backoff before surfacing the error.
|
|
87
95
|
- **Stale advisory lock**: a prior run crashed. The next run detects the dead PID and reclaims the lock automatically. If it does not (very rare — the recorded PID was recycled by another live process), delete `<parent-of-dest>/.flow-skill.installing.lock`.
|
|
88
96
|
- **`No matching version found` on `@0.1.x`**: you are trying to install a pre-release version through a stable range. Use `@rc` (dist-tag) or an explicit `@0.1.0-rc.N` until stable `0.1.0` ships.
|
|
97
|
+
- **`No matching version found` on `@0.22.0`**: that is the **skill product** version, not the npm package version. Use `@rc` / `@0.1.0-rc.N` (installer) — skill content version is printed by `--help` as `ships skill v…`.
|
|
98
|
+
- **Installed package but agent has no `/flow`**: `npm i` does not run the installer. Run `npx @manhquy/flow-skill@rc` (or `npx flow-skill` if already installed) and restart the agent.
|
|
89
99
|
- **Node too old** (`requires Node.js >=22.14.0`): upgrade with your preferred version manager (`nvm install 22`, `fnm install 22`, or Node's official installer). Node 20 reached end-of-life April 2026; npm OIDC Trusted Publishing needs npm >=11.5.1 which bundles with Node 22.14+.
|
|
90
100
|
|
|
91
101
|
## JSONL contract
|
|
@@ -93,7 +103,7 @@ rm -rf <project>/.claude/skills/flow
|
|
|
93
103
|
`--json` streams one JSON object per line:
|
|
94
104
|
|
|
95
105
|
```jsonl
|
|
96
|
-
{"event":"plan","version":"0.1.0-rc.
|
|
106
|
+
{"event":"plan","version":"0.1.0-rc.3","skillVersion":"0.22.0","dryRun":false,"scope":"global","targets":["claude","codex"]}
|
|
97
107
|
{"event":"install:start","target":"claude","dests":["~/.claude/skills/flow"]}
|
|
98
108
|
{"event":"install:done","target":"claude","dests":["~/.claude/skills/flow"],"result":"success","error":null,"warnings":[]}
|
|
99
109
|
{"event":"summary","success":true,"total":2,"attempted":2,"installed":2,"failed":0,"skipped":0,"aborted":false}
|
|
@@ -105,7 +115,7 @@ rm -rf <project>/.claude/skills/flow
|
|
|
105
115
|
|
|
106
116
|
| Event | Required fields | Notes |
|
|
107
117
|
|---|---|---|
|
|
108
|
-
| `plan` | `version` (string), `dryRun` (bool), `scope` (`global`\|`project`), `targets` (string[]) | Always the first event. |
|
|
118
|
+
| `plan` | `version` (string), `dryRun` (bool), `scope` (`global`\|`project`), `targets` (string[]), optional `skillVersion` (string\|null) | Always the first event. `version` = npm package; `skillVersion` = bundled `SKILL.md` product version. |
|
|
109
119
|
| `install:start` | `target` (string), `dests` (string[]) | One per selected target. |
|
|
110
120
|
| `install:done` | `target`, `dests`, `result` (`success`\|`failed`), `error` (string\|null), `warnings` (string[]) | Fired after each target. `error` is `null` on success. |
|
|
111
121
|
| `summary` | `success` (bool), `total`, `attempted`, `installed`, `failed`, `skipped`, `aborted` (bool) | Always the last event. Counts sum: `attempted = installed + failed`; `skipped = total - attempted`. |
|
package/README_VN.md
CHANGED
|
@@ -10,13 +10,15 @@ Trình cài đặt một-lệnh sao chép skill [flow](https://github.com/manhqu
|
|
|
10
10
|
## Cài đặt
|
|
11
11
|
|
|
12
12
|
```
|
|
13
|
-
# Kênh pre-release (hiện tại — v0.1.0-rc.
|
|
13
|
+
# Kênh pre-release (hiện tại — v0.1.0-rc.3, ships skill v0.22.0)
|
|
14
14
|
npx @manhquy/flow-skill@rc
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Prompt tương tác hỏi cài vào agent nào. Chọn một hoặc nhiều, xác nhận, xong.
|
|
18
18
|
|
|
19
|
-
> **Giai đoạn RC**: pin `@rc` (dist-tag) hoặc phiên bản cụ thể `@0.1.0-rc.
|
|
19
|
+
> **Giai đoạn RC**: pin `@rc` (dist-tag) hoặc phiên bản cụ thể `@0.1.0-rc.3`. `npx @manhquy/flow-skill@0.1.x` chỉ dùng được sau khi stable `0.1.0` publish — semver range **không** match pre-release theo mặc định. Xem [SECURITY.md](./SECURITY.md).
|
|
20
|
+
>
|
|
21
|
+
> **`npm i` alone chưa đủ** — phải **chạy** CLI (`npx @manhquy/flow-skill@rc`). Hai trục version: package npm vs skill product (`ships skill v…` / `skillVersion` trong JSONL).
|
|
20
22
|
|
|
21
23
|
## Non-interactive
|
|
22
24
|
|
|
@@ -110,6 +112,11 @@ Mỗi phiên bản publish có [npm provenance](https://docs.npmjs.com/generatin
|
|
|
110
112
|
npm view @manhquy/flow-skill@<version> dist.attestations.provenance
|
|
111
113
|
```
|
|
112
114
|
|
|
115
|
+
> **Ngoại lệ RC-window**: `v0.1.0-rc.1` được publish thủ công từ máy developer để bootstrap npm
|
|
116
|
+
> Trusted Publisher (TP không thể bind tới package chưa tồn tại) — bản này không có provenance.
|
|
117
|
+
> Từ `v0.1.0-rc.2` trở đi, mọi bản publish đều qua CI workflow với OIDC và có attestation
|
|
118
|
+
> SLSA Build Level 2 đầy đủ.
|
|
119
|
+
|
|
113
120
|
## Bảo mật
|
|
114
121
|
|
|
115
122
|
Threat model + WILL/WON'T list + kênh report — xem [SECURITY.md](./SECURITY.md). Tóm tắt: pure Node, không network call, không spawn shell/PowerShell, không postinstall hook, không chạm settings/hooks/MCP config.
|
package/SECURITY.md
CHANGED
|
@@ -55,7 +55,7 @@ These match the behavior of the upstream `install.sh`; they are documented rathe
|
|
|
55
55
|
```
|
|
56
56
|
npm view @manhquy/flow-skill@<version> dist.attestations.provenance
|
|
57
57
|
```
|
|
58
|
-
**`v0.1.0-rc.1` exception**: this bootstrap version was published manually (Trusted Publisher requires the package to already exist before it can bind) and carries no attestation.
|
|
58
|
+
**`v0.1.0-rc.1` exception**: this bootstrap version was published manually (Trusted Publisher requires the package to already exist before it can bind) and carries no attestation. `v0.1.0-rc.2` (2026-07-17) is the first version published from CI via OIDC — confirmed live with an SLSA Build Level 2 attestation. All versions from rc.2 onward are attested; if provenance is a hard requirement, pin `@0.1.0-rc.2` or later.
|
|
59
59
|
- **Publisher account.** npm 2FA `auth-and-writes` is enabled as defense-in-depth for the git + npm identity behind the workflow.
|
|
60
60
|
- **Pin your version.** During the RC window use `@rc` (dist-tag) or an explicit `@0.1.0-rc.N`. After stable ships, pin `@0.1.x` (or a specific version). `@latest` invites blind updates on a package that writes to your agent home.
|
|
61
61
|
- **Completeness signal (not tamper detection).** The tarball ships `skills-manifest.json` recording the synced skill's file count + list. The publish workflow re-counts the checked-out tree against this manifest to catch an incomplete sync. It is **not** a supply-chain integrity proof — that role belongs to npm provenance above. `.integrity` SHA-256 sidecar was intentionally not adopted because it would have been generated from the same source it hashed (circular trust).
|
package/bin/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
6
|
|
|
7
|
-
import { TARGETS, TARGET_NAMES, PKG_VERSION } from '../src/constants.mjs';
|
|
7
|
+
import { TARGETS, TARGET_NAMES, PKG_VERSION, SKILL_VERSION } from '../src/constants.mjs';
|
|
8
8
|
import { defaultSelection, detectAll } from '../src/detect.mjs';
|
|
9
9
|
import { renderHelp } from '../src/help.mjs';
|
|
10
10
|
import { install, installAntigravity } from '../src/installer.mjs';
|
|
@@ -288,6 +288,9 @@ async function main() {
|
|
|
288
288
|
{
|
|
289
289
|
event: 'plan',
|
|
290
290
|
version: PKG_VERSION,
|
|
291
|
+
// Additive: skill product version (SKILL.md). `version` stays the npm package version
|
|
292
|
+
// for back-compat with existing JSONL consumers.
|
|
293
|
+
skillVersion: SKILL_VERSION,
|
|
291
294
|
dryRun: opts.dryRun,
|
|
292
295
|
scope,
|
|
293
296
|
targets: plan.map((p) => p.target),
|
package/package.json
CHANGED
package/skills/flow/SKILL.md
CHANGED
|
@@ -8,7 +8,7 @@ keywords: [flow, buildflow, gate, build, ship, scope, prd, contract, card, deplo
|
|
|
8
8
|
license: MIT
|
|
9
9
|
metadata:
|
|
10
10
|
author: flow-skill
|
|
11
|
-
version: "0.
|
|
11
|
+
version: "0.24.0"
|
|
12
12
|
attribution: "Methodology from ai20k-build-phase/buildflow (Tony, arealisticdreamer.com); harness/agent layers from repository-harness, claudekit-engineer, BMAD-METHOD. v0.22 concierge routing pattern adapted from BMAD-METHOD's bmad-help; forge-idea ritual adapted from BMAD-METHOD's bmad-forge-idea (both MIT, BMad Code LLC)."
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -235,7 +235,7 @@ Hard stops (iteration/token/time caps) and ground-truth gates (`flow.sh` exit, r
|
|
|
235
235
|
- `references/gate-eval.md` — `/flow eval` behavioral proof for the semantic layer: what it
|
|
236
236
|
measures (a fresh-judge lower bound, NOT the work-mode self-challenge), cost, thresholds,
|
|
237
237
|
and the honest authorship-bias limitation. Read before running or relaying a scorecard.
|
|
238
|
-
- `harness/` — durable layer (`flow.sh harness ...`): intake/story
|
|
238
|
+
- `harness/` — durable layer (`flow.sh harness ...`): intake/story/**complete**/trace/decision/backlog. Pins: protocol floor `harness-cli-v0.1.14`, trust CLI `0.1.17` (never `0.1.16`). See `harness/README.md` + `harness/GAP-MATRIX-0.1.17.md`. Never `story update --status implemented` — use `story complete --proof-source …`.
|
|
239
239
|
- `playbooks/` — paid-for stack knowledge: read before building a card on that stack, harvest the lesson after.
|
|
240
240
|
- `_templates/` — the 7 artifacts the runner copies into `flow/` and `cards/`. Never edit during a run.
|
|
241
241
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Gap matrix: flow durable layer vs repository-harness CLI 0.1.17
|
|
2
|
+
|
|
3
|
+
**Authority:** `repository-harness` (local `D:\project\flow\repository-harness`).
|
|
4
|
+
**Pins:** protocol floor **`harness-cli-v0.1.14`** · trust/consumer CLI **`harness-cli-v0.1.17`** · **never** install/use **`0.1.16`** release assets (tag without published proof).
|
|
5
|
+
**Scope:** inventory only — does **not** claim US-101 bit-identical or protocol-v1 parity for flow Python.
|
|
6
|
+
|
|
7
|
+
Supersedes: brainstorm 260703 Thread1 (“nothing to upgrade”).
|
|
8
|
+
|
|
9
|
+
## Schema
|
|
10
|
+
|
|
11
|
+
| Ver | Flow | Harness 0.1.17 | Notes |
|
|
12
|
+
|-----|------|----------------|-------|
|
|
13
|
+
| 001–004 | present (shared) | present | Hash-identical at last measure (2026-07-18) |
|
|
14
|
+
| 005 | present | present | File hash may differ on comments only — do not overclaim DDL fork |
|
|
15
|
+
| 006–008 | **absent** (reserved gap) | changesets / deps / hierarchy | Not adopted this plan |
|
|
16
|
+
| 009–012 | **usage/accessed** (flow-only) | improvement identity / links / closure | **Semantic collision** — same numbers, different DDL |
|
|
17
|
+
| 013 | absent | changeset content sha | Not adopted |
|
|
18
|
+
|
|
19
|
+
## Commands / invariants
|
|
20
|
+
|
|
21
|
+
| Surface | Flow status | Harness 0.1.17 | Trust note |
|
|
22
|
+
|---------|-------------|----------------|------------|
|
|
23
|
+
| `story update --status implemented` | rejected (trust-align) | rejected (US-101) | Use `story complete` |
|
|
24
|
+
| `story complete` | flow-native flags + honest `proof_source` | `story complete` + live verify | Flow may complete via `card_markdown_gate` **without** setting `last_verified_result=pass` |
|
|
25
|
+
| `story verify` | optional shell verify_command | required for shell-proven complete | shell=True = operator-authored only |
|
|
26
|
+
| `query sql` | **not exposed** | read-only connection | Do not add mutating SQL |
|
|
27
|
+
| Rust forward | refused on flow-lineage DB | n/a | `FLOW_HARNESS_BACKEND=rust` → exit 2 when usage mirror / schema≥9 |
|
|
28
|
+
| Changesets / work-graph | not ported | 006–013 | Out of scope (FOMO red line) |
|
|
29
|
+
| `query contract --json` | not on Python CLI | protocol v1 discovery | Optional external binary smoke only |
|
|
30
|
+
|
|
31
|
+
## Pins (consumer)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
HARNESS_PROTOCOL_V1_TAG = harness-cli-v0.1.14 # floor
|
|
35
|
+
HARNESS_CLI_TRUST_TAG = harness-cli-v0.1.17 # trust features + release proof
|
|
36
|
+
DO_NOT_USE = harness-cli-v0.1.16 # no assets / failed promotion
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Rust refuse-forward
|
|
40
|
+
|
|
41
|
+
Any DB with `usage_event` or `MAX(schema_version) >= 9` is **flow-lineage**. Python entrypoint must refuse forwarding to external `harness-cli` (exit 2). Never unfreeze on `.flow/harness.db` without schema re-home ≥014 (separate epic).
|
|
@@ -2,10 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
The durable memory of `/flow`: intake classification, story packets with proof status,
|
|
4
4
|
auto-scored execution traces, decision records, and the growth-rule backlog. Ported from
|
|
5
|
-
`repository-harness` (
|
|
5
|
+
`repository-harness` (shared base schema + flow-only usage extensions). State lives at
|
|
6
6
|
`<project>/.flow/harness.db` and survives across sessions — this is the "external memory"
|
|
7
7
|
that fights context rot.
|
|
8
8
|
|
|
9
|
+
## Authority pins (repository-harness)
|
|
10
|
+
|
|
11
|
+
| Pin | Tag | Use |
|
|
12
|
+
|-----|-----|-----|
|
|
13
|
+
| Protocol floor | **`harness-cli-v0.1.14`** | protocol v1 discovery floor |
|
|
14
|
+
| Trust / consumer | **`harness-cli-v0.1.17`** | US-101 spirit + release proof |
|
|
15
|
+
| **Do not use** | `harness-cli-v0.1.16` | tag without published assets |
|
|
16
|
+
|
|
17
|
+
Gap inventory: [`GAP-MATRIX-0.1.17.md`](./GAP-MATRIX-0.1.17.md). Flow does **not** claim bit-identical US-101.
|
|
18
|
+
|
|
9
19
|
## Backends
|
|
10
20
|
|
|
11
21
|
| Backend | When | How |
|
|
@@ -23,12 +33,36 @@ power-path for non-flow DBs; flow does not build or ship the binary.
|
|
|
23
33
|
|
|
24
34
|
Disable the durable layer entirely with `FLOW_HARNESS_DISABLE=1` (engine still runs).
|
|
25
35
|
|
|
36
|
+
### STRICT durable writes (`flow.sh`)
|
|
37
|
+
|
|
38
|
+
| Env | Behavior |
|
|
39
|
+
|-----|----------|
|
|
40
|
+
| unset | soft: engine exit 0; one-line `flow-harness: warn` on fail |
|
|
41
|
+
| `FLOW_HARNESS_STRICT=1` | soft exit; louder stderr |
|
|
42
|
+
| `FLOW_HARNESS_STRICT=fail` | propagate nonzero from durable ops on card/check |
|
|
43
|
+
|
|
44
|
+
## Story completion (trust boundary)
|
|
45
|
+
|
|
46
|
+
- **Rejected:** `story update --status implemented`
|
|
47
|
+
- **Use:** `story complete --id <id> --proof-source card_markdown_gate|manual|verify_command [--evidence …]`
|
|
48
|
+
- `card_markdown_gate` / `manual` set status implemented with **honest** `proof_source=…` in notes — **never** forge `last_verified_result=pass`
|
|
49
|
+
- `verify_command` requires prior `story verify` pass
|
|
50
|
+
|
|
51
|
+
## Versioning (harness vs npm)
|
|
52
|
+
|
|
53
|
+
- Telemetry / usage events carry **`flow_version` from the installed skill product**
|
|
54
|
+
(`SKILL.md` metadata), not from the npm installer package version.
|
|
55
|
+
- When you change harness schema or CLI behavior, bump the **skill product** version
|
|
56
|
+
(SKILL.md + plugin.json + portable-manifest) and run `flow.sh coherence`.
|
|
57
|
+
- Publishing a new npm tarball is a separate step (`npm-wrapper` sync + tag `npm@…`) —
|
|
58
|
+
see [`docs/release-process.md`](../../../docs/release-process.md).
|
|
59
|
+
|
|
26
60
|
## Commands ↔ Runtime-Substrate responsibility
|
|
27
61
|
|
|
28
62
|
| Command | Responsibility (of 11) |
|
|
29
63
|
|---|---|
|
|
30
64
|
| `intake --type <t> --summary <s> [--flags ...] [--lane ...] [--narrow-scope]` | Task specification + risk classification |
|
|
31
|
-
| `story add\|update\|verify\|verify-all` | Task state + Verification (mechanical proof) |
|
|
65
|
+
| `story add\|update\|complete\|verify\|verify-all` | Task state + Verification (mechanical proof) |
|
|
32
66
|
| `trace --summary ... [--story\|--card C-NNN] [--agent ...] [--actions ...] [--files-changed ...] --outcome ...` | Observability + Failure attribution (auto-scored tier). Flags accept `-` or `_` (`--actions_taken`, `--files_changed`, `--files_read`) and `--card` is an alias of `--story`. A bad/missing flag prints a guiding "common forms" hint (not a silent exit-2). |
|
|
33
67
|
| `decision add\|verify\|outcome` | Project memory (durable ADR row + companion markdown); `outcome` closes the predicted-vs-actual loop |
|
|
34
68
|
| `backlog add\|close` | Entropy auditing + harness self-improvement (growth rule) |
|
|
@@ -60,14 +94,14 @@ Linking a trace to an unverified story prints a **pre-close gate** warning.
|
|
|
60
94
|
- `/flow next` past **stage 04 (ADR)** → reminds you to record each decision durably (`decision add`) — the ADR markdown is not a durable record.
|
|
61
95
|
- `/flow card` creates a card → seeds a `story` row (tracking handle).
|
|
62
96
|
- `/flow check C-NNN` (todo) → `story update --status in_progress`.
|
|
63
|
-
- `/flow check C-NNN` (done) → `story
|
|
97
|
+
- `/flow check C-NNN` (done) → `story complete --proof-source card_markdown_gate` + a `trace` (tier verdict shown; honest provenance, no shell-verify stamp).
|
|
64
98
|
- `/flow recall` → reads the durable layer back (friction + backlog + audit health) into your working context — the capture→reuse loop.
|
|
65
99
|
- `/flow retro` → surfaces `propose` (deterministic improvement proposals from repeated friction/interventions + audit drift) for you to commit.
|
|
66
100
|
- `/flow harness <args>` exposes the full CLI directly.
|
|
67
101
|
All wiring degrades gracefully: if python is absent or `FLOW_HARNESS_DISABLE=1`, the
|
|
68
102
|
mechanical engine runs unchanged.
|
|
69
103
|
|
|
70
|
-
## Usage signal: accessed_count (read-only, schema
|
|
104
|
+
## Usage signal: accessed_count (read-only, schema 009 — see Backends re-homing note above)
|
|
71
105
|
`query decisions|friction|backlog` increment an `accessed_count` on every surfaced row and order
|
|
72
106
|
output **most-reused-first**, so `recall` floats the knowledge you actually reuse to the top. It is
|
|
73
107
|
a **read-only ordering signal** — never used to prune, delete, or archive a row. Low count is not
|
|
@@ -76,7 +110,7 @@ rare*. Security-class rows (`auth|authoriz|admin|tenan|payment|migrat|valid|secr
|
|
|
76
110
|
always sort first and are never deprioritized. The story `matrix` view is intentionally left
|
|
77
111
|
ordered by id (it is a status table, not recalled knowledge).
|
|
78
112
|
|
|
79
|
-
## Usage log: mechanical flight-recorder (schema
|
|
113
|
+
## Usage log: mechanical flight-recorder (schema 010 — see Backends re-homing note above)
|
|
80
114
|
Distinct from the curated, agent-authored records above: `flow.sh` itself self-records **every
|
|
81
115
|
invocation** (no agent action needed) to append-only JSONL — `.flow/events.jsonl` (full, per-project)
|
|
82
116
|
plus `~/.claude/flow/usage.jsonl` (compact, device-global). Fields: ts/epoch_s, session_id, cycle_id
|
|
@@ -93,7 +127,7 @@ stage_from→to, card, project_type, mode, flow_version, tier, host, read_only.
|
|
|
93
127
|
- JSONL is the source of truth; `usage_event` is a derived, queryable mirror. Semantic events keep
|
|
94
128
|
using `trace`/`intervention`/`decision` — the usage log does not duplicate them.
|
|
95
129
|
|
|
96
|
-
**Closed feedback loop (schema
|
|
130
|
+
**Closed feedback loop (schema 011 — see Backends re-homing note above).** The recorded data feeds the surfaces where you already act:
|
|
97
131
|
- `recall` appends a one-line digest (`flow_harness.py usage --summary`): cycles, cycle-time, gate
|
|
98
132
|
fail-rate, top gate-fail stage — so build history reaches you at stage/card start (silent if no data).
|
|
99
133
|
- `propose` (`_build_proposals`) emits a backlog proposal when a stage's gate fail-rate ≥ 50% across
|
|
@@ -160,6 +160,21 @@ def cmd_intake(con, a):
|
|
|
160
160
|
return 0
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
_PROOF_SOURCES = ("card_markdown_gate", "manual", "verify_command")
|
|
164
|
+
_SECRET_RE = re.compile(
|
|
165
|
+
r"(?i)(token|secret|passwd|password|credential|api[_-]?key|bearer|authorization|-----begin)"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _mask_evidence(text):
|
|
170
|
+
"""Redact secret-shaped free text before durable store (trust-align RT)."""
|
|
171
|
+
if text is None:
|
|
172
|
+
return None
|
|
173
|
+
if _SECRET_RE.search(text):
|
|
174
|
+
return "***redacted***"
|
|
175
|
+
return text
|
|
176
|
+
|
|
177
|
+
|
|
163
178
|
def cmd_story(con, a):
|
|
164
179
|
if a.story_cmd == "add":
|
|
165
180
|
if a.lane not in D.LANES:
|
|
@@ -169,11 +184,58 @@ def cmd_story(con, a):
|
|
|
169
184
|
print(f"PASS: story {a.id} added (lane={a.lane})")
|
|
170
185
|
return 0
|
|
171
186
|
if a.story_cmd == "update":
|
|
187
|
+
# Trust boundary (US-101 spirit, flow-native): bare implemented is forbidden.
|
|
188
|
+
if a.status == "implemented":
|
|
189
|
+
print(
|
|
190
|
+
"FAIL: story update --status implemented is rejected.\n"
|
|
191
|
+
" Use: story complete --id <id> --proof-source card_markdown_gate|manual|verify_command\n"
|
|
192
|
+
" (honest proof provenance; never forge last_verified_result without story verify)"
|
|
193
|
+
)
|
|
194
|
+
return 1
|
|
172
195
|
n = _db.update(con, "story", "id", a.id, status=a.status,
|
|
173
196
|
unit_proof=a.unit, integration_proof=a.integration,
|
|
174
|
-
e2e_proof=a.e2e, platform_proof=a.platform,
|
|
197
|
+
e2e_proof=a.e2e, platform_proof=a.platform,
|
|
198
|
+
evidence=_mask_evidence(a.evidence))
|
|
175
199
|
print(f"{'PASS' if n else 'FAIL'}: story {a.id} {'updated' if n else 'not found'}")
|
|
176
200
|
return 0 if n else 1
|
|
201
|
+
if a.story_cmd == "complete":
|
|
202
|
+
s = _db.one(con, "SELECT * FROM story WHERE id=?", (a.id,))
|
|
203
|
+
if not s:
|
|
204
|
+
print(f"FAIL: story {a.id} not found"); return 1
|
|
205
|
+
src = (a.proof_source or "").strip()
|
|
206
|
+
if src not in _PROOF_SOURCES:
|
|
207
|
+
print(
|
|
208
|
+
"FAIL: --proof-source required: card_markdown_gate|manual|verify_command\n"
|
|
209
|
+
" card_markdown_gate = /flow check markdown gate (does NOT set last_verified=pass)\n"
|
|
210
|
+
" verify_command = requires prior story verify pass"
|
|
211
|
+
)
|
|
212
|
+
return 1
|
|
213
|
+
if src == "verify_command":
|
|
214
|
+
if s.get("last_verified_result") != "pass":
|
|
215
|
+
print(
|
|
216
|
+
f"FAIL: proof-source verify_command requires last_verified_result=pass "
|
|
217
|
+
f"(got {s.get('last_verified_result')!r}). Run: story verify --id {a.id}"
|
|
218
|
+
)
|
|
219
|
+
return 1
|
|
220
|
+
# Honest provenance: card/manual never forge last_verified_result=pass
|
|
221
|
+
ev = _mask_evidence(a.evidence) or s.get("evidence") or ""
|
|
222
|
+
note = (s.get("notes") or "").strip()
|
|
223
|
+
prov = f"proof_source={src}"
|
|
224
|
+
if prov not in note:
|
|
225
|
+
note = (note + "\n" + prov).strip() if note else prov
|
|
226
|
+
if src == "card_markdown_gate" and "last_verified_result=pass" not in (note or ""):
|
|
227
|
+
# explicit honesty marker for auditors
|
|
228
|
+
note = note + "\nverify_stamp=not_shell"
|
|
229
|
+
fields = dict(status="implemented", notes=note)
|
|
230
|
+
if ev:
|
|
231
|
+
fields["evidence"] = ev
|
|
232
|
+
# only keep last_verified_* when real verify already passed
|
|
233
|
+
n = _db.update(con, "story", "id", a.id, **fields)
|
|
234
|
+
print(
|
|
235
|
+
f"PASS: story {a.id} complete (status=implemented, proof_source={src})"
|
|
236
|
+
+ ("" if src == "verify_command" else " [no shell-verify stamp]")
|
|
237
|
+
)
|
|
238
|
+
return 0 if n else 1
|
|
177
239
|
if a.story_cmd in ("verify", "verify-all"):
|
|
178
240
|
if a.story_cmd == "verify":
|
|
179
241
|
stories = [_db.one(con, "SELECT * FROM story WHERE id=?", (a.id,))]
|
|
@@ -983,6 +1045,13 @@ def build_parser():
|
|
|
983
1045
|
a2.add_argument("--evidence")
|
|
984
1046
|
a3 = pss.add_parser("verify"); a3.add_argument("--id", required=True)
|
|
985
1047
|
pss.add_parser("verify-all")
|
|
1048
|
+
# flow-native complete (trust-align D7): not protocol-v1 positional parity
|
|
1049
|
+
ac = pss.add_parser("complete", help="mark implemented with honest proof_source (not bare update)")
|
|
1050
|
+
ac.add_argument("--id", required=True)
|
|
1051
|
+
ac.add_argument("--proof-source", dest="proof_source",
|
|
1052
|
+
choices=("card_markdown_gate", "manual", "verify_command"),
|
|
1053
|
+
help="card_markdown_gate never forges last_verified=pass")
|
|
1054
|
+
ac.add_argument("--evidence")
|
|
986
1055
|
|
|
987
1056
|
pt = sub.add_parser("trace", help="record an agent task execution trace (auto-scored)")
|
|
988
1057
|
pt.add_argument("--summary", required=True); pt.add_argument("--intake", type=int); pt.add_argument("--story", "--card")
|
|
@@ -1034,7 +1103,7 @@ def build_parser():
|
|
|
1034
1103
|
q1 = pqs.add_parser("matrix"); q1.add_argument("--json", action="store_true"); q1.add_argument("--numeric", action="store_true")
|
|
1035
1104
|
q2 = pqs.add_parser("backlog"); q2.add_argument("--open", action="store_true"); q2.add_argument("--closed", action="store_true"); q2.add_argument("--json", action="store_true")
|
|
1036
1105
|
q3 = pqs.add_parser("friction"); q3.add_argument("--json", action="store_true")
|
|
1037
|
-
q4 = pqs.add_parser("tools"); q4.add_argument("--responsibility"); q4.add_argument("--json", action="store_true")
|
|
1106
|
+
q4 = pqs.add_parser("tools"); q4.add_argument("--responsibility"); q4.add_argument("--json", action="store_true")
|
|
1038
1107
|
q4.add_argument("--capability"); q4.add_argument("--status", choices=D.TOOL_STATUSES)
|
|
1039
1108
|
q5 = pqs.add_parser("decisions"); q5.add_argument("--json", action="store_true")
|
|
1040
1109
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Digests from GitHub Release harness-cli-v0.1.17 (fetched for pin policy)
|
|
2
|
+
# TAG harness-cli-v0.1.17
|
|
3
|
+
# PROTOCOL_FLOOR harness-cli-v0.1.14
|
|
4
|
+
# DO_NOT_USE harness-cli-v0.1.16
|
|
5
|
+
5d13f0a024d9d69701534a879581c9433bc0069e04d052e5aacd93b91b3b7a43 harness-cli-linux-x64
|
|
6
|
+
128c42c251fb31b58582342c09c68be6ed3329340fb1d8f179c88590a4539c97 harness-cli-linux-arm64
|
|
7
|
+
902968f1383d45eb60e202eb98f8ce6c143cff73cb6f2ccc159a743ca5a70819 harness-cli-macos-x64
|
|
8
|
+
71217dbe03d111775c5016ab751f8bedbff66e7fd6a4d90f5c8d7e0e4a205b47 harness-cli-macos-arm64
|
|
9
|
+
2d7edff22ae25308c2ba23ab85bb9acb18d87eea28a9ca4efb2014a0f11a51a6 harness-cli-windows-x64.exe
|
|
@@ -54,7 +54,7 @@ gate): **`ck-predict` at ADR** (5-persona pre-decision debate) and **`ck-scenari
|
|
|
54
54
|
| UI card | `ui-ux-designer` | — | inline + `law/DESIGN.md` | review vs DESIGN.md |
|
|
55
55
|
| Repair / diagnostic | `debugger` | — | inline root-cause + fresh same-ladder subagent | `harness intervention add` |
|
|
56
56
|
| Review | `code-reviewer` (+ `typescript-reviewer` or `python-reviewer` layered — see language-specialist lens) | `bmad-code-review` (3-layer adversarial) | inline | `harness intervention add` on red |
|
|
57
|
-
| Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story update --status implemented` |
|
|
57
|
+
| Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story complete --id … --proof-source manual` (never bare `update --status implemented`) |
|
|
58
58
|
| Docs sync | `docs-manager` | — | inline doc update | — |
|
|
59
59
|
| Verify-live | `tester` / `web-testing` | `bmad-qa-generate-e2e-tests` | curl/Playwright | `harness story update --e2e 1` + `trace` |
|
|
60
60
|
|
|
@@ -34,7 +34,7 @@ for each todo card in card-number order:
|
|
|
34
34
|
4. flow.sh check C-NNN must PASS (mechanical) + gate-rules semantic check
|
|
35
35
|
5. merge to main in card order; deploy; VERIFY ON LIVE URL (merge != shipped)
|
|
36
36
|
6. paste world-state evidence into the card; status: done
|
|
37
|
-
7. flow.sh harness story
|
|
37
|
+
7. flow.sh harness story complete --id … --proof-source manual (or card_markdown_gate) + trace; log AUTO-LOG.md
|
|
38
38
|
8. remove worktree
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -180,15 +180,71 @@ _python() {
|
|
|
180
180
|
return 1
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
# Durable write honesty (plan trust-align D5/D8):
|
|
184
|
+
# unset FLOW_HARNESS_STRICT → soft: engine stays 0; one-line stderr warn on fail
|
|
185
|
+
# FLOW_HARNESS_STRICT=1 → soft exit; louder multi-line stderr
|
|
186
|
+
# FLOW_HARNESS_STRICT=fail → propagate nonzero from harness (card/check paths)
|
|
187
|
+
# harness_call_checked always returns the real harness exit (for callers that care).
|
|
188
|
+
_harness_run() {
|
|
189
|
+
# runs harness; prints stderr (masked lightly); echoes nothing; returns real rc
|
|
190
|
+
# usage: _harness_run <args...> sets _HARNESS_LAST_RC
|
|
191
|
+
_HARNESS_LAST_RC=0
|
|
185
192
|
[ -n "${FLOW_HARNESS_DISABLE:-}" ] && return 0
|
|
186
193
|
[ -f "$HARNESS_PY" ] || return 0
|
|
187
194
|
local py; py="$(_python)"; [ -n "$py" ] || return 0
|
|
188
|
-
|
|
195
|
+
local errf out rc=0
|
|
196
|
+
errf="$(mktemp 2>/dev/null || echo "$ROOT/.flow/.harness-err.$$")"
|
|
197
|
+
mkdir -p "$ROOT/.flow" 2>/dev/null || true
|
|
198
|
+
# capture rc without flipping the caller's set -e state
|
|
199
|
+
out="$(FLOW_PROJECT_ROOT="$ROOT" "$py" "$HARNESS_PY" "$@" 2>"$errf")" || rc=$?
|
|
200
|
+
_HARNESS_LAST_RC=$rc
|
|
201
|
+
if [ "$rc" -ne 0 ]; then
|
|
202
|
+
local errline summary
|
|
203
|
+
errline="$(tr -d '\r' <"$errf" 2>/dev/null | head -n 3 | tr '\n' ' ')"
|
|
204
|
+
# light redact of secret-shaped stderr / never dump free-text evidence args
|
|
205
|
+
case "$errline" in
|
|
206
|
+
*token*|*secret*|*passwd*|*password*|*credential*|*api_key*|*bearer*) errline="***redacted***" ;;
|
|
207
|
+
esac
|
|
208
|
+
summary="$1"
|
|
209
|
+
[ -n "${2:-}" ] && summary="$summary $2"
|
|
210
|
+
[ -n "${3:-}" ] && summary="$summary $3"
|
|
211
|
+
local mode="${FLOW_HARNESS_STRICT:-}"
|
|
212
|
+
if [ "$mode" = "1" ] || [ "$mode" = "fail" ]; then
|
|
213
|
+
printf 'flow-harness: durable command failed (rc=%s) cmd=%s\nflow-harness: %s\n' \
|
|
214
|
+
"$rc" "$summary" "$errline" >&2
|
|
215
|
+
else
|
|
216
|
+
printf 'flow-harness: warn durable write failed (rc=%s)\n' "$rc" >&2
|
|
217
|
+
fi
|
|
218
|
+
fi
|
|
219
|
+
rm -f "$errf" 2>/dev/null || true
|
|
220
|
+
return "$rc"
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
harness_call() {
|
|
224
|
+
# soft durable write: NEVER breaks engine unless STRICT=fail
|
|
225
|
+
_harness_run "$@" || true
|
|
226
|
+
if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ] && [ "${_HARNESS_LAST_RC:-0}" -ne 0 ]; then
|
|
227
|
+
return "${_HARNESS_LAST_RC}"
|
|
228
|
+
fi
|
|
189
229
|
return 0
|
|
190
230
|
}
|
|
191
231
|
|
|
232
|
+
harness_call_checked() {
|
|
233
|
+
# returns real harness exit code (and still prints STRICT-aware warnings)
|
|
234
|
+
_harness_run "$@"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# When STRICT=fail, surface durable failure from card/check call sites (D5).
|
|
238
|
+
_harness_or_fail() {
|
|
239
|
+
harness_call "$@" || {
|
|
240
|
+
if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ]; then
|
|
241
|
+
echo "FAIL: durable harness write failed (FLOW_HARNESS_STRICT=fail)."
|
|
242
|
+
return 1
|
|
243
|
+
fi
|
|
244
|
+
return 0
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
192
248
|
harness_available() { # 0 = durable layer usable (python + harness present, not disabled)
|
|
193
249
|
[ -n "${FLOW_HARNESS_DISABLE:-}" ] && return 1
|
|
194
250
|
[ -f "$HARNESS_PY" ] || return 1
|
|
@@ -973,7 +1029,7 @@ cmd_card_start() { # mark a card actively in progress (operator-visible in_progr
|
|
|
973
1029
|
local id; id="$(basename "$file" .md)"; FLOW_LOG_CARD="$id"
|
|
974
1030
|
if [ "$(card_status "$file")" = "done" ]; then echo "FAIL: $id is already done — nothing to start."; return 1; fi
|
|
975
1031
|
_inflight_set "$id"
|
|
976
|
-
|
|
1032
|
+
_harness_or_fail story update --id "$id" --status in_progress # durable mirror (STRICT=fail propagates)
|
|
977
1033
|
echo "PASS: $id marked in flight. Build it, then '/flow card done $id' (or hand-edit status: done + '/flow check $id')."
|
|
978
1034
|
return 0
|
|
979
1035
|
}
|
|
@@ -1033,7 +1089,7 @@ cmd_card() {
|
|
|
1033
1089
|
[ -n "$pc" ] && printf '%s\n' "$pc" | sed 's/^/ /'
|
|
1034
1090
|
[ -n "$dbt" ] && { echo " open debt:"; printf '%s\n' "$dbt" | sed 's/^/ /'; }
|
|
1035
1091
|
fi
|
|
1036
|
-
|
|
1092
|
+
_harness_or_fail story add --id "$id" --title "$id" --lane normal # durable tracking handle
|
|
1037
1093
|
return 0
|
|
1038
1094
|
}
|
|
1039
1095
|
|
|
@@ -1106,9 +1162,22 @@ cmd_check() {
|
|
|
1106
1162
|
if [ "$found" -eq 0 ]; then
|
|
1107
1163
|
echo "PASS: $id is valid (status: $st)."
|
|
1108
1164
|
case "$st" in
|
|
1109
|
-
todo)
|
|
1110
|
-
done)
|
|
1111
|
-
|
|
1165
|
+
todo) _harness_or_fail story update --id "$id" --status in_progress || return 1 ;;
|
|
1166
|
+
done)
|
|
1167
|
+
# trust-align: complete with honest card_markdown_gate provenance (D3/D7/D8)
|
|
1168
|
+
if ! harness_call_checked story complete --id "$id" \
|
|
1169
|
+
--proof-source card_markdown_gate \
|
|
1170
|
+
--evidence "card $id markdown gate PASS"; then
|
|
1171
|
+
if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ]; then
|
|
1172
|
+
echo "FAIL: durable story complete failed (FLOW_HARNESS_STRICT=fail)."
|
|
1173
|
+
return 1
|
|
1174
|
+
fi
|
|
1175
|
+
fi
|
|
1176
|
+
# auto-trace: enrich tier-2 fields from known card context (do not fake --lane tiny)
|
|
1177
|
+
local tr
|
|
1178
|
+
tr="$(harness_emit trace --summary "card $id reached done-evidence" --story "$id" \
|
|
1179
|
+
--outcome completed --agent flow-check \
|
|
1180
|
+
--actions "check $id gate PASS" --files-changed "cards/$id.md" --friction none)"
|
|
1112
1181
|
[ -n "$tr" ] && printf '%s\n' "$tr" | grep -iE 'tier|to reach' | sed 's/^/ harness: /' ;;
|
|
1113
1182
|
esac
|
|
1114
1183
|
return 0
|
package/skills-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "../skills/flow",
|
|
3
3
|
"destRelative": "skills/flow",
|
|
4
|
-
"fileCount":
|
|
5
|
-
"syncedAt": "2026-07-
|
|
4
|
+
"fileCount": 76,
|
|
5
|
+
"syncedAt": "2026-07-19T14:46:19.032Z",
|
|
6
6
|
"fileList": [
|
|
7
7
|
"SKILL.md",
|
|
8
8
|
"_templates/00-idea.md",
|
|
@@ -31,11 +31,13 @@
|
|
|
31
31
|
"eval/fixtures/routing/states/state-review-pending.txt",
|
|
32
32
|
"eval/fixtures/routing/states/state-scope-stage.txt",
|
|
33
33
|
"eval/manifest.tsv",
|
|
34
|
+
"harness/GAP-MATRIX-0.1.17.md",
|
|
34
35
|
"harness/README.md",
|
|
35
36
|
"harness/_db.py",
|
|
36
37
|
"harness/_domain.py",
|
|
37
38
|
"harness/_presence.py",
|
|
38
39
|
"harness/flow_harness.py",
|
|
40
|
+
"harness/pins/harness-cli-v0.1.17.sha256sums",
|
|
39
41
|
"harness/repo_map.py",
|
|
40
42
|
"harness/schema/001-init.sql",
|
|
41
43
|
"harness/schema/002-story-verify.sql",
|
package/src/constants.mjs
CHANGED
|
@@ -72,11 +72,26 @@ export const CLI_NAME = 'flow-skill';
|
|
|
72
72
|
|
|
73
73
|
// F3 fix: read version from package.json at runtime so `npm version` bumps propagate to the
|
|
74
74
|
// JSON `plan` event without a hand-edit here.
|
|
75
|
-
import { readFileSync } from 'node:fs';
|
|
75
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
76
76
|
import { fileURLToPath } from 'node:url';
|
|
77
77
|
import { dirname, resolve } from 'node:path';
|
|
78
78
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
79
|
+
const pkgRoot = resolve(__dirname, '..');
|
|
79
80
|
const pkgJson = JSON.parse(
|
|
80
|
-
readFileSync(resolve(
|
|
81
|
+
readFileSync(resolve(pkgRoot, 'package.json'), 'utf8')
|
|
81
82
|
);
|
|
82
83
|
export const PKG_VERSION = pkgJson.version;
|
|
84
|
+
|
|
85
|
+
// Skill product version (SKILL.md metadata.version) is a SEPARATE axis from the npm
|
|
86
|
+
// installer package version. Surface both so `npx … --help` / plan events cannot be
|
|
87
|
+
// misread as "stale skill" when the user expected 0.22.x and saw 0.1.0-rc.N.
|
|
88
|
+
function readSkillVersion() {
|
|
89
|
+
const skillMd = resolve(pkgRoot, 'skills', 'flow', 'SKILL.md');
|
|
90
|
+
if (!existsSync(skillMd)) return null;
|
|
91
|
+
const text = readFileSync(skillMd, 'utf8');
|
|
92
|
+
// Frontmatter only — first `version: "x.y.z"` under metadata (or any YAML version line).
|
|
93
|
+
// Indented under `metadata:` in YAML frontmatter (two spaces typical).
|
|
94
|
+
const m = text.match(/^\s*version:\s*["']?([0-9][0-9A-Za-z.+-]*)["']?\s*$/m);
|
|
95
|
+
return m ? m[1] : null;
|
|
96
|
+
}
|
|
97
|
+
export const SKILL_VERSION = readSkillVersion();
|
package/src/help.mjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { CLI_NAME, PKG_VERSION, TARGETS } from './constants.mjs';
|
|
1
|
+
import { CLI_NAME, PKG_VERSION, SKILL_VERSION, TARGETS } from './constants.mjs';
|
|
2
2
|
|
|
3
3
|
export function renderHelp() {
|
|
4
4
|
const targetRows = TARGETS.map((t) => ` ${t.name.padEnd(12)} ${t.label}`).join('\n');
|
|
5
5
|
// v0.23 H5 (red-team): derive the count from TARGETS.length so a future target addition can
|
|
6
6
|
// never again leave a stale hardcoded number in the help text.
|
|
7
7
|
const targetCount = TARGETS.length;
|
|
8
|
-
|
|
8
|
+
const skillBit = SKILL_VERSION ? ` (ships skill v${SKILL_VERSION})` : '';
|
|
9
|
+
return `${CLI_NAME} v${PKG_VERSION}${skillBit}
|
|
9
10
|
|
|
10
11
|
Install the flow skill into your coding agent(s).
|
|
11
12
|
|
|
12
13
|
USAGE
|
|
13
|
-
npx @manhquy/flow-skill
|
|
14
|
+
npx @manhquy/flow-skill@rc
|
|
15
|
+
# or after stable ships: npx @manhquy/flow-skill
|
|
14
16
|
|
|
15
17
|
OPTIONS
|
|
16
18
|
-y, --yes Skip prompts; install to default selection (detected + Claude)
|