verity-framework 0.2.0 → 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/README.md +58 -59
- package/package.json +1 -1
- package/verity/bin/lib/release.cjs +43 -7
- package/verity/bin/lib/smoke.cjs +29 -2
package/README.md
CHANGED
|
@@ -2,93 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
**Prompt to production, proven.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Most AI coding tools stop when the code is written — leaving you to find out later whether it runs, deploys, or actually works. **Verity keeps going.** It carries a project from an idea all the way to software that is tested, deployed, and **proven working in front of a real user** — and then keeps it running.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
It does that by running your project as a sequence of specialized AI roles — a vision assistant, an architect, a builder, a reviewer, a release operator, a verifier, and more — that hand work to each other through clear contracts, with **GitHub as the single source of truth**. It's CI/CD-native and GitHub-native by design, built for projects going *beyond a prototype* into real, operated production. (Verity is a clean-room successor to [spec-driven-devops](https://www.npmjs.com/package/spec-driven-devops) 1.4.)
|
|
8
8
|
|
|
9
|
-
>
|
|
9
|
+
> 📦 On npm as [`verity-framework`](https://www.npmjs.com/package/verity-framework) · works with **Claude Code** and **OpenCode**.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## What makes it different
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
- **"Done" means proven, not written.** Every change is reviewed, deployed, and driven like a real user before it counts as finished.
|
|
14
|
+
- **The AI decides; a deterministic tool records.** The `verity` CLI is the "notebook" that never forgets — it holds the rules and the official state, so a forgetful model (or a brand-new chat) can always pick up exactly where things left off.
|
|
15
|
+
- **State is read from GitHub, never a file that drifts.** A merged PR *is* "built"; a tag *is* "released." Nothing to hand-maintain, nothing to lie about.
|
|
16
|
+
- **Specialist roles with real handoffs** beat one mega-prompt trying to do everything.
|
|
17
|
+
|
|
18
|
+
## How it works
|
|
19
|
+
|
|
20
|
+
A Verity project moves through three arcs:
|
|
21
|
+
|
|
22
|
+
1. **Bootstrap** *(once)* — name the project, create the repo, design the architecture, choose where it deploys, and prove the whole build→ship pipeline on a tiny end-to-end "walking skeleton."
|
|
23
|
+
2. **Stream** *(the loop)* — every feature flows through the same short cycle: **plan → build → review → merge**, riding that proven pipeline.
|
|
24
|
+
3. **Operate** *(continuous)* — cut releases, deploy, **verify on the live app**, and keep it healthy.
|
|
25
|
+
|
|
26
|
+
Five subsystems carry that out:
|
|
27
|
+
|
|
28
|
+
| Subsystem | Job |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Relay** | Orchestrates the roles, the stream loop, and the dependency engine |
|
|
31
|
+
| **Shipyard** | The CI/CD spine — releases, deploys, the deployment-methods catalog, and runtime truth (`STATUS.md`) |
|
|
32
|
+
| **Ledger** | Derives state from GitHub, so nothing goes stale |
|
|
33
|
+
| **Gate** | Review, security, and the quality gates |
|
|
34
|
+
| **Verify** | Live "observably-works" verification |
|
|
35
|
+
|
|
36
|
+
## Install
|
|
17
37
|
|
|
18
|
-
**Prerequisites:** a GitHub account · Node ≥16 · `git` and the GitHub CLI (`gh`) installed **and signed in** (`gh auth login` — installing `gh` is not the same as being authenticated).
|
|
38
|
+
**Prerequisites:** a GitHub account · Node ≥16 · `git` and the GitHub CLI (`gh`) installed **and signed in** (run `gh auth login` once — installing `gh` is not the same as being authenticated).
|
|
19
39
|
|
|
20
40
|
```bash
|
|
41
|
+
# preflight — all three should answer without error:
|
|
21
42
|
node -v && git --version && gh auth status
|
|
43
|
+
|
|
44
|
+
# install, then connect it to your assistant:
|
|
45
|
+
npm i -g verity-framework
|
|
46
|
+
verity install --claude # or: --opencode
|
|
22
47
|
```
|
|
23
48
|
|
|
24
|
-
Then, in your AI assistant, start a project with `/verity:vision`.
|
|
49
|
+
Then, in your AI assistant, start a project with `/verity:vision`. The [interactive guides](#guides) walk through it step by step.
|
|
25
50
|
|
|
26
51
|
## Deployment methods
|
|
27
52
|
|
|
28
|
-
**
|
|
29
|
-
|
|
30
|
-
**Why it exists.** Without it, an AI agent picks a host by whatever happens to be lying around — e.g. suggesting Render.com simply because a Render tool is installed in its environment. That's not a decision; it's a coincidence. The catalog makes the Architect ask *"here's what you can deploy to — which one for this app?"*, and record the answer as an [ADR](docs/commands.md). It also keeps host and credential details **out of git** while still letting a team share them safely.
|
|
53
|
+
Verity makes **where your app deploys a deliberate choice**, not an accident. Left alone, an AI agent picks a host by whatever happens to be wired into its environment — suggesting Render.com, say, just because a Render tool is installed. The deployment-methods catalog fixes that: the **Architect** reads your saved targets and works with you to choose one (recording it as an ADR), and if none are configured yet it asks how you want to deploy and offers suggestions.
|
|
31
54
|
|
|
32
|
-
**
|
|
55
|
+
It's **two files, two scopes** — and crucially, no secrets ever touch git:
|
|
33
56
|
|
|
34
57
|
| | Global catalog | Per-app access |
|
|
35
58
|
|---|---|---|
|
|
36
59
|
| **Path** | `~/.verity/deployment-methods.md` | `.verity/deploy-access.md` |
|
|
37
60
|
| **Scope** | You, across *every* project | One app |
|
|
38
61
|
| **Holds** | The deploy targets you *can* use | How to reach *this* app's host |
|
|
39
|
-
| **Created** | At `verity install`
|
|
40
|
-
| **In git?** | No
|
|
41
|
-
|
|
42
|
-
> 🔒 **Both files reference credential *locations*, never secrets.** Point at a key file (`~/.ssh/prod.pem`), an SSO profile, or a secret-store entry — never paste an actual key, password, or token. The real per-app file is shared out-of-band (not through the repo), so nothing sensitive ever lands in git history. (Same rule as `STATUS.md`.)
|
|
43
|
-
|
|
44
|
-
**How to use it.** The global catalog is seeded at install with two worked examples (AWS EC2 over SSH, and a local-network server) — edit it to describe *your* real targets:
|
|
62
|
+
| **Created** | At `verity install` — seeded once, never overwritten | Written by the Architect, per app |
|
|
63
|
+
| **In git?** | No (lives in your home dir) | **No — gitignored.** A committed, secret-free pointer tells teammates who lack it to ask the project admin |
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
verity deployment list # what targets you have (the Architect reads this)
|
|
48
|
-
verity deployment show aws-ec2 # one method's details
|
|
49
|
-
verity deployment path # where the catalog file lives
|
|
50
|
-
verity deployment edit # open it in $EDITOR to add/adjust targets
|
|
51
|
-
```
|
|
65
|
+
> 🔒 Both files reference credential **locations** — a key file like `~/.ssh/prod.pem`, an SSO profile, a secret-store entry — never an actual key, password, or token. The per-app file is shared out-of-band, so nothing sensitive lands in git history. (Same rule as `STATUS.md`.)
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
The global catalog ships with two worked examples (AWS EC2 over SSH, and a local-network server). Edit it to describe your real targets:
|
|
54
68
|
|
|
55
69
|
```bash
|
|
56
|
-
verity deployment
|
|
57
|
-
verity deployment
|
|
70
|
+
verity deployment list # your targets (this is what the Architect reads)
|
|
71
|
+
verity deployment show aws-ec2 # one target's details
|
|
72
|
+
verity deployment edit # open the catalog in $EDITOR
|
|
73
|
+
verity deployment path # where the catalog lives
|
|
58
74
|
```
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## Guides (interactive, beginner-friendly)
|
|
63
|
-
|
|
64
|
-
Self-contained HTML — clone/download the repo and open them in any browser (no server or internet needed):
|
|
65
|
-
|
|
66
|
-
- [`docs/verity-overview.html`](docs/verity-overview.html) — **Overview**: what Verity is, the mental model, how it works (no jargon assumed)
|
|
67
|
-
- [`docs/verity-usage.html`](docs/verity-usage.html) — **Usage**: install + command-by-command recipes + pro tips (Claude Code / OpenCode toggle)
|
|
68
|
-
- [`docs/verity-flows.html`](docs/verity-flows.html) — **Flows**: start-from-scratch vs add-to-an-existing-project, side by side
|
|
69
|
-
- [`docs/verity-flows.drawio`](docs/verity-flows.drawio) — the flow diagram as an editable draw.io / diagrams.net file
|
|
70
|
-
- [`docs/commands.md`](docs/commands.md) — **Command reference**: all 13 `/verity:*` role commands and what each one does
|
|
71
|
-
- [`docs/explainer-kit.md`](docs/explainer-kit.md) — **Explainer kit**: a briefing for an AI to describe Verity to humans (podcast/deck/talk) — story, diagrams, soundbites, fact sheet
|
|
76
|
+
When the Architect picks a target it sets up the per-app access file — `verity deployment init-access` gitignores the real file and commits the "ask the admin" pointer — then writes `.verity/deploy-access.md` with how to reach this app's host. From there, `/verity:ship` deploys to that target. (`verity deployment access` tells a teammate whether they have the file, and who to ask if not.)
|
|
72
77
|
|
|
73
|
-
##
|
|
74
|
-
- **Relay** — role orchestration + the stream loop + dependency engine
|
|
75
|
-
- **Shipyard** — CI/CD spine + Release/Deploy Operator + deployment-methods catalog + runtime truth (`STATUS.md`)
|
|
76
|
-
- **Ledger** — GitHub-derived state engine (no stale files)
|
|
77
|
-
- **Gate** — review + security + the quality gates
|
|
78
|
-
- **Verify** — live "observably-works" verification
|
|
78
|
+
## Guides
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
- [`docs/framework-spec.md`](docs/framework-spec.md) — the build-ready architecture spec
|
|
82
|
-
- [`docs/roles-spec.md`](docs/roles-spec.md) — working log + full rationale (all roles)
|
|
83
|
-
- [`docs/interview-findings.md`](docs/interview-findings.md) — forensic interview of the real build that drove the design
|
|
84
|
-
- [`docs/brand.md`](docs/brand.md) — naming / positioning
|
|
85
|
-
- [`docs/walking-skeleton-plan.md`](docs/walking-skeleton-plan.md) — the first implementation slice
|
|
80
|
+
Interactive, beginner-friendly, and fully self-contained — clone or download the repo and open them in any browser (no server or internet needed):
|
|
86
81
|
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
- [**Overview**](docs/verity-overview.html) — what Verity is and the mental model, no jargon assumed
|
|
83
|
+
- [**Usage**](docs/verity-usage.html) — install + command-by-command recipes + pro tips (Claude Code / OpenCode toggle)
|
|
84
|
+
- [**Flows**](docs/verity-flows.html) — start-from-scratch vs. add-to-an-existing-project, side by side ([editable `.drawio`](docs/verity-flows.drawio))
|
|
85
|
+
- [**Command reference**](docs/commands.md) — all 13 `/verity:*` roles and what each one does
|
|
86
|
+
- [**Explainer kit**](docs/explainer-kit.md) — a briefing for an AI to describe Verity to humans (podcast / deck / talk)
|
|
89
87
|
|
|
90
|
-
##
|
|
91
|
-
See [`CONTRIBUTING.md`](CONTRIBUTING.md) — local setup, the test/lint checks, project layout, and conventions.
|
|
88
|
+
## Reference
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
- **Package:** `verity-framework` · CLI binary `verity` · Node ≥16 · host deps `git`, `gh`
|
|
91
|
+
- **Design docs:** [framework spec](docs/framework-spec.md) · [roles spec](docs/roles-spec.md) · [the interview that drove the design](docs/interview-findings.md) · [brand / positioning](docs/brand.md) · [walking-skeleton plan](docs/walking-skeleton-plan.md)
|
|
92
|
+
- **Contributing:** [`CONTRIBUTING.md`](CONTRIBUTING.md) — local setup, the test/lint checks, project layout, and conventions
|
|
93
|
+
- **License:** MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verity-framework",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Verity — a CI/CD-native, GitHub-native, production-lifecycle AI software delivery framework.",
|
|
5
5
|
"keywords": ["verity", "ai", "ci-cd", "github", "devops", "agent", "framework"],
|
|
6
6
|
"author": "Sean Mahoney",
|
|
@@ -75,18 +75,34 @@ function changelogFrom(commits, version) {
|
|
|
75
75
|
return lines.join('\n').trim();
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// Prepend a changelog section, returning a rollback that restores the file to its
|
|
79
|
+
// exact prior state (content, or non-existence) — so a later failure can undo it.
|
|
78
80
|
function prependChangelog(cwd, section) {
|
|
79
81
|
const p = path.join(cwd, 'CHANGELOG.md');
|
|
82
|
+
const existedBefore = fs.existsSync(p);
|
|
83
|
+
const before = existedBefore ? fs.readFileSync(p, 'utf8') : null;
|
|
80
84
|
const header = '# Changelog';
|
|
81
|
-
const existing =
|
|
85
|
+
const existing = before ? before.replace(header, '').trim() : '';
|
|
82
86
|
const body = `${header}\n\n${section}\n\n${existing}`.trim();
|
|
83
87
|
fs.writeFileSync(p, `${body}\n`);
|
|
88
|
+
return () => {
|
|
89
|
+
if (existedBefore) {
|
|
90
|
+
fs.writeFileSync(p, before);
|
|
91
|
+
} else {
|
|
92
|
+
fs.rmSync(p, { force: true });
|
|
93
|
+
}
|
|
94
|
+
};
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
function run(cmd, args) {
|
|
87
98
|
execFileSync(cmd, args, { stdio: 'inherit' });
|
|
88
99
|
}
|
|
89
100
|
|
|
101
|
+
// A release has three side effects (tag, changelog edit, push) that must be
|
|
102
|
+
// all-or-nothing: a half-done release leaves either a dirty CHANGELOG.md with no
|
|
103
|
+
// tag, or a local tag that never pushed. We order them cheap-and-reversible-first
|
|
104
|
+
// (tag → changelog → push) and roll back the earlier steps if a later one throws.
|
|
105
|
+
// The git runner is injectable (opts.run) so partial failure is unit-testable.
|
|
90
106
|
function cut(cwd, opts = {}) {
|
|
91
107
|
const tags = opts.tags || gitTags(cwd);
|
|
92
108
|
const previous = ledger.latestTag(tags);
|
|
@@ -94,14 +110,34 @@ function cut(cwd, opts = {}) {
|
|
|
94
110
|
const tag = `v${version}`;
|
|
95
111
|
const commits = opts.commits || commitsSince(cwd, previous);
|
|
96
112
|
const changelog = changelogFrom(commits, version);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
const result = { version, tag, previous, changelog, commitCount: commits.length };
|
|
114
|
+
if (opts.dryRun) {
|
|
115
|
+
return { ...result, applied: false };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const exec = opts.run || run;
|
|
119
|
+
exec('git', ['-C', cwd, 'tag', tag]); // step 1 — if this throws, nothing changed yet
|
|
120
|
+
|
|
121
|
+
let restoreChangelog;
|
|
122
|
+
try {
|
|
123
|
+
restoreChangelog = prependChangelog(cwd, changelog); // step 2
|
|
124
|
+
} catch (err) {
|
|
125
|
+
exec('git', ['-C', cwd, 'tag', '-d', tag]); // roll back step 1
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (opts.push !== false) {
|
|
130
|
+
try {
|
|
131
|
+
exec('git', ['-C', cwd, 'push', 'origin', tag]); // step 3
|
|
132
|
+
} catch (err) {
|
|
133
|
+
restoreChangelog(); // roll back step 2
|
|
134
|
+
exec('git', ['-C', cwd, 'tag', '-d', tag]); // roll back step 1
|
|
135
|
+
throw new Error(
|
|
136
|
+
`release push failed — rolled back tag ${tag} and CHANGELOG.md, working tree is clean. Original error: ${err.message}`,
|
|
137
|
+
);
|
|
102
138
|
}
|
|
103
139
|
}
|
|
104
|
-
return {
|
|
140
|
+
return { ...result, applied: true };
|
|
105
141
|
}
|
|
106
142
|
|
|
107
143
|
function current(cwd) {
|
package/verity/bin/lib/smoke.cjs
CHANGED
|
@@ -78,9 +78,36 @@ function buildScript(baseUrl, flow) {
|
|
|
78
78
|
return lines.join('\n');
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function
|
|
81
|
+
function resolvableFrom(pkg, cwd) {
|
|
82
|
+
try {
|
|
83
|
+
require.resolve(pkg, { paths: [cwd, process.cwd()] });
|
|
84
|
+
return true;
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function onPath(bin, pathDirs) {
|
|
91
|
+
const exts = process.platform === 'win32' ? ['.cmd', '.exe', '.bat', ''] : [''];
|
|
92
|
+
return pathDirs.some((d) => exts.some((e) => fs.existsSync(path.join(d, bin + e))));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Capability probe — is a headless browser usable here? Checks three honest sources
|
|
96
|
+
// so we don't false-skip when the tool is installed in a non-local layout:
|
|
97
|
+
// 1. the project's local node_modules/.bin
|
|
98
|
+
// 2. resolvable as a package from cwd (hoisted / monorepo / npm-linked global)
|
|
99
|
+
// 3. a CLI on PATH (a globally-installed playwright)
|
|
100
|
+
// Pure (no execution). If none match it returns unavailable, and runSmoke degrades
|
|
101
|
+
// to a non-pass — never a false green. Callers can bypass detection entirely by
|
|
102
|
+
// injecting opts.probe / opts.driver into runSmoke (e.g. a project-specific runner).
|
|
103
|
+
function defaultProbe(cwd, env = process.env) {
|
|
104
|
+
const pathDirs = (env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
82
105
|
for (const bin of ['playwright', 'puppeteer']) {
|
|
83
|
-
if (
|
|
106
|
+
if (
|
|
107
|
+
fs.existsSync(path.join(cwd, 'node_modules', '.bin', bin)) ||
|
|
108
|
+
resolvableFrom(bin, cwd) ||
|
|
109
|
+
onPath(bin, pathDirs)
|
|
110
|
+
) {
|
|
84
111
|
return { available: true, tool: bin };
|
|
85
112
|
}
|
|
86
113
|
}
|