flowviant 0.28.0 → 0.28.2

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.
@@ -130,25 +130,62 @@ Ground EVERY claim in files you actually read (Read, Grep, Glob, ls, git in the
130
130
  repo) — never guess.
131
131
 
132
132
  THE HUMAN DOCS — docs/ inside the vault. After the vault pages are current,
133
- COMPILE human documentation FROM them (distill your own vault pages — don't
134
- re-read the whole repo; spot-check a cited file only when something looks off).
135
- Docs are for humans: clear prose, short sections, a reading order. Fixed spine:
136
- - docs/00-start-here.md "Start Here": what this codebase is, how to run it,
137
- the handful of files that matter most, where to go next.
138
- - docs/01-architecture.md the big picture: major pieces, how they fit, data
139
- flow, and a map of the chapters below.
140
- - docs/1N-<chapter>.md — ONE chapter per major subsystem (10, 11, 12 …), YOUR
141
- choice of chapters, derived from the vault's hub pages.
142
- - docs/90-decisions.md — notable design decisions and their why.
143
- - docs/91-glossary.md the project's terms of art.
144
- Docs pages use the same frontmatter files: lists and [[wikilinks]] (they may
145
- link to vault pages); numeric prefixes are the reading order.
133
+ COMPILE professional developer documentation FROM them (distill your own vault
134
+ pages; spot-check a cited file only when something looks off — don't re-read the
135
+ whole repo). These are what a new engineer onboards from and a working engineer
136
+ keeps open: hold them to the standard of Stripe / Google / Microsoft developer
137
+ docs comprehensive, precisely structured, richly cross-linked. Detailed and
138
+ thorough beats short: a reader should be able to work in a subsystem after
139
+ reading its chapter.
140
+
141
+ Fixed spine (numeric prefix = reading order):
142
+ - docs/00-start-here.md — the landing page + MASTER TABLE OF CONTENTS: what the
143
+ product is (2-3 sentences); how to run it locally (prerequisites, install,
144
+ required env, dev server, tests); then a linked table of contents of EVERY
145
+ chapter, each with a one-line description; then 2-3 role-based reading paths
146
+ (e.g. "New to the backend: read 01, then 12, then 14").
147
+ - docs/01-architecture.md — the system at a glance: a Mermaid diagram (a fenced
148
+ code block whose language is mermaid) of the major components and how they
149
+ connect, a component-responsibility table, the primary request/data flows, and
150
+ a link into the chapter for each component.
151
+ - docs/1N-<chapter>.md — ONE chapter per major subsystem (10, 11, 12 …), your
152
+ choice of chapters, derived from the vault's hub pages. Cover every significant
153
+ subsystem.
154
+ - docs/90-decisions.md — notable design decisions, each as context, decision,
155
+ why, and consequences.
156
+ - docs/91-glossary.md — the project's terms of art, alphabetized, each linking to
157
+ the chapter or vault page that defines it.
158
+
159
+ EVERY chapter follows this exact anatomy, in order:
160
+ 1. YAML frontmatter listing the real repo files the chapter draws on.
161
+ 2. A "# Title" heading.
162
+ 3. One or two sentences: what the chapter covers and who should read it.
163
+ 4. A "## Contents" section — an in-page table of contents: a bulleted list
164
+ linking each of the chapter's own "## " sections by anchor. An anchor is the
165
+ heading text lowercased, spaces turned to hyphens, punctuation removed — so
166
+ a section "## How dispatch works" is linked "- [How dispatch works](#how-dispatch-works)".
167
+ 5. The body sections ("## " / "### "), including as relevant: an overview and
168
+ where the subsystem sits in the system; how it works walked step by step
169
+ with REAL code excerpts (fenced and language-tagged) and file citations; a
170
+ Mermaid diagram for any non-trivial flow or sequence; and REFERENCE TABLES
171
+ for the concrete surface — HTTP endpoints (method, path, auth, purpose), key
172
+ functions/types, env/config keys, DB tables/columns — as markdown tables.
173
+ 6. A "## Gotchas" section: the traps, edge cases, invariants, and non-obvious
174
+ constraints.
175
+ 7. A "## See also" section: [[wikilinks]] to the deeper vault pages, plus
176
+ relative links to sibling chapters (e.g. "[Architecture](01-architecture.md)").
177
+
178
+ Cross-link liberally: [[wikilinks]] point to vault pages; relative "NN-name.md"
179
+ links point to sibling chapters; both are clickable in the reader. Keep every
180
+ claim grounded in code you actually read.
146
181
 
147
182
  Full-sweep protocol:
148
183
  1. If the vault already has pages, read index.md + log.md FIRST — update and
149
184
  extend rather than rewrite; delete vault pages whose code no longer exists.
150
185
  2. Explore the repo broadly, then write/refresh pages area by area.
151
- 3. Compile/refresh the docs/ chapters from the finished vault pages.
186
+ 3. Compile/refresh the docs/ chapters from the finished vault pages, following
187
+ the docs spine + per-chapter anatomy above (Contents TOC, reference tables,
188
+ Mermaid diagrams, Gotchas, See also).
152
189
  4. Refresh index.md, append a log.md entry, then output exactly WIKI_DONE on
153
190
  its own line and stop.
154
191
 
@@ -60,14 +60,19 @@ export async function reportDeployConfig(repoRoot) {
60
60
  const targets = readDeployConfig(repoRoot);
61
61
  const json = JSON.stringify(targets);
62
62
  if (json === lastConfigJson) return;
63
- // Strip commands/secrets before the server sees the config (metadata only).
63
+ // Scrub command strings before the server sees them a command line can embed
64
+ // an internal host or a synced secret. Only redacted metadata leaves the box.
65
+ const scrubCmds = (o) =>
66
+ o && typeof o === 'object'
67
+ ? Object.fromEntries(Object.entries(o).map(([k, v]) => [k, scrub(String(v ?? ''))]))
68
+ : o;
64
69
  const meta = targets.map((t) => ({
65
70
  id: t.id,
66
71
  label: t.label,
67
72
  provider: t.provider || 'cloudflare',
68
- command: t.command,
69
- build: t.build,
70
- commands: t.commands,
73
+ command: scrub(String(t.command ?? '')),
74
+ build: t.build ? scrub(String(t.build)) : t.build,
75
+ commands: scrubCmds(t.commands),
71
76
  healthcheck: t.healthcheck,
72
77
  healthStatus: t.healthStatus,
73
78
  pushSecrets: t.pushSecrets,
@@ -100,6 +105,11 @@ function run(command, { cwd, env, input }) {
100
105
  /* already gone */
101
106
  }
102
107
  }, 30 * 60_000);
108
+ // A broken pipe (child exits before draining stdin — e.g. a fast-failing
109
+ // `wrangler secret put`) surfaces as an ASYNC 'error' on the stdin stream,
110
+ // which the try/catch below can't catch. Without a listener it's an uncaught
111
+ // exception that kills the whole daemon. Swallow it.
112
+ child.stdin.on('error', () => {});
103
113
  if (input != null) {
104
114
  try {
105
115
  child.stdin.write(input);
@@ -129,7 +139,7 @@ async function verifyHealth(url, status) {
129
139
  for (let i = 0; i < 4; i++) {
130
140
  try {
131
141
  const res = await fetch(url, { signal: AbortSignal.timeout(10_000), redirect: 'manual' });
132
- if (res.status === status) return true;
142
+ if (res.status === Number(status)) return true; // coerce — a string "200" in deploy.json must still match
133
143
  } catch {
134
144
  /* not up yet */
135
145
  }
@@ -148,6 +158,10 @@ const claiming = new Set(); // in-flight guard (single-flight per daemon process
148
158
  export function processDeployJobs(jobs, ctx) {
149
159
  if (!Array.isArray(jobs) || !jobs.length) return;
150
160
  for (const job of jobs) {
161
+ // Defend against a malformed roster element — `job.id` on a null would throw
162
+ // synchronously here (outside the per-job try below) and wedge the whole
163
+ // reconcile loop, since this runs unguarded from the fleet tick.
164
+ if (!job || typeof job.id !== 'string') continue;
151
165
  if (claiming.has(job.id)) continue;
152
166
  claiming.add(job.id);
153
167
  void (async () => {
@@ -185,6 +199,7 @@ export function processDeployJobs(jobs, ctx) {
185
199
 
186
200
  async function runDeploy(job, target, ctx) {
187
201
  const env = { ...process.env, ...deployCreds() }; // inject infra creds; never a file
202
+ delete env.FLEET_TOKEN; // the deploy command has no business reading it; keep it out of a command that might echo its env
188
203
  const logs = [];
189
204
  // Rollback is a single wrangler command; deploy is build → secrets → deploy.
190
205
  if (job.kind === 'rollback') {
package/bin/lib/fleet.mjs CHANGED
@@ -295,6 +295,7 @@ export async function runFleetDaemon() {
295
295
  };
296
296
  const processMergeJobs = (jobs) => {
297
297
  for (const job of jobs ?? []) {
298
+ if (!job || typeof job.id !== 'string') continue; // a null element would wedge the loop
298
299
  if (merging.has(job.id)) continue;
299
300
  merging.add(job.id);
300
301
  (async () => {
@@ -374,6 +375,7 @@ export async function runFleetDaemon() {
374
375
  const cleaning = new Set();
375
376
  const processCleanupJobs = (jobs) => {
376
377
  for (const job of jobs ?? []) {
378
+ if (!job || typeof job.id !== 'string') continue; // a null element would wedge the loop
377
379
  if (cleaning.has(job.id)) continue;
378
380
  cleaning.add(job.id);
379
381
  (async () => {
@@ -889,7 +891,10 @@ export async function runFleetDaemon() {
889
891
  // turn) until we report reground-done; the bare drain flushes anything
890
892
  // whose earlier mint failed.
891
893
  enqueueSweep(roster.codeMapJob);
892
- for (const j of roster.regroundJobs ?? []) enqueueReground(j.intentId, j.prUrl, j.title);
894
+ for (const j of roster.regroundJobs ?? []) {
895
+ if (!j || typeof j.intentId !== 'string') continue; // a null element would throw + wedge the loop
896
+ enqueueReground(j.intentId, j.prUrl, j.title);
897
+ }
893
898
  void drainWiki();
894
899
 
895
900
  // Env sync tick: register/bootstrap/wrap/rotate/sync as the roster block
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowviant",
3
- "version": "0.28.0",
3
+ "version": "0.28.2",
4
4
  "description": "Run your own Claude Code as headless build agents for Flowviant — on your own credentials. Claims dispatched work, opens PRs, captures review evidence, and routes questions back to you.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  "homepage": "https://flowviant.com",
31
31
  "repository": {
32
32
  "type": "git",
33
- "url": "https://github.com/flowviant/cli.git"
33
+ "url": "git+https://github.com/flowviant/cli.git"
34
34
  },
35
35
  "license": "MIT",
36
36
  "bugs": {