diff --git a/CHANGELOG.md b/CHANGELOG.md index 26ab5467..fef9e0fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,33 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/). ## [Unreleased] ### Added +- **v0.3 alpha — local file ingest + run chains.** `stacy brain create --file` + and `--dir`/`--glob`/`--ext` turn local text/markdown/json files into signed + Knowledge Objects with **no connector and no credentials** (leak-safe + cwd-relative source labels; dotfiles/`node_modules`/`.git`/symlinks/binary/ + oversized files skipped). `stacy run --chain ` composes multi-step + runs where a later step consumes an earlier step's `agent_output` via an + `@` reference (validated before egress, gated once, one-hop + provenance, abort-on-step-failure). Together they make the whole ingest → run + → synthesize loop runnable offline. See + `docs/v0.3-files-and-chains-quickstart.md`. + +### Fixed +- **Run-result caching now composes across chain steps** (verified live). Two + changes: (1) `agent_output` content no longer embeds a wall-clock + `generatedAt`; (2) the run cache keys inputs on their **content**, not on the + KO `contentHash` (which folds in `createdAt`, so it changes every time a KO is + created even for identical content). Without (2) a chain's downstream step — + whose input is the prior step's freshly-created output KO — missed the cache + on every run. Now an identical chain re-run makes **zero adapter calls**. KO + `contentHash` is still used for provenance; generation time still lives on the + KO record and the `run` receipt. + +### Changed +- Extracted a reusable `runOnce` run executor (single-run verb and run chains + share it); single-run behavior is unchanged. + +### Added (v0.2 alpha) - **v0.2 alpha — connectors + AI runs.** GitHub connector (OAuth device-code flow, encrypted-at-rest token storage, sliding-window rate limiting), the `stacy connect` / `connectors list|status|disconnect` / `ingest` verbs that diff --git a/TODOS.md b/TODOS.md new file mode 100644 index 00000000..c784d7a9 --- /dev/null +++ b/TODOS.md @@ -0,0 +1,93 @@ +# TODOS + +Deferred work, captured so it is not silently lost. Each item has enough context +to pick up cold. + +## Later — Generic ingest refactor + `fs`/Linear as real Connectors + +- **What:** Refactor `verbs/ingest.ts` (currently `ingestGithubCommand`, GitHub- + specific) into a generic registry-driven `ingestCommand(connectorId, params)`, + and implement `fs` as a real `Connector` (with `authType: "none"`). +- **Why deferred:** Eng review (Codex outside voice #12) showed this is premature + for the alpha. `brain create --file` (v0.3 W1) unblocks the zero-credential loop + without touching connector auth/registry/connect/status. The framework refactor + only pays off once a second OAuth connector (Linear) justifies it. +- **Scope when revisited (holes the review found):** `authType: "none"` is a core + interface change — `Connector.authenticate/status/ingest` and + `IngestOptions.token` all currently require `TokenBundle` + (`connectors/types.ts:44,77`). `connectors list/status` derive "connected" from + keychain entries, so a no-auth connector needs a status story. The confirmation + count currently forces full enumeration before storing — reconcile with + streaming. Command registration lives in `packages/federation/verbs/index.ts`, + not just `cli/src/index.ts`. +- **Depends on:** a concrete second connector (Linear) to justify the work. + +## ~~Bug — Postgres NOTICE lines pollute `--json` stdout~~ — FIXED 2026-06-03 + +- **What:** Federation commands self-create tables/indexes with + `CREATE INDEX IF NOT EXISTS`; on an existing DB Postgres emitted a `NOTICE` + ("relation ... already exists, skipping") that the `postgres` driver printed to + **stdout**, ahead of the JSON, breaking `--json` for strict parsers. +- **Fix:** `createDb` in `packages/db/src/client.ts` now passes + `onnotice: () => {}` (matching the existing `createUtilitySql` factory). + Verified live before/after: `brain create --file --json` against an + initialized DB now emits only the JSON object on stdout (`python3 -m + json.tool` parses it cleanly; before, three NOTICE objects preceded it). + +## Observation — KO content hash folds in `createdAt` + +- Not a bug (the v0.3 run cache now keys on content, so chain caching works), but + worth recording: `knowledge-object.ts:57,60` includes `createdAt` in the + hashed payload, so identical content created at different times yields + different KO ids/hashes. If true content-addressing is ever wanted + (re-creating identical content → same id), that is a core-model change needing + its own review (federation/consent/dedup implications). Deferred. + +## Later — `brain lineage ` graph-walk command + +- **What:** A command/visualizer that walks `provenance.inputKoIds` edges to show + a KO's full lineage back to its original inputs. +- **Why:** v0.3 uses one-hop provenance (each KO lists direct inputs only). Full + lineage is *derivable* by walking edges but there's no tool to do it. Eng + review (#9) flagged that a graph-walk exit criterion needs a real walker. +- **Where:** new `brain lineage` verb over `agent-output` content `inputKoIds` + + run receipts. + +## v0.3.1 — Linear connector (deferred from Phase 3 / W2) + +- **What:** Add a Linear connector (issues → signed `document`/`issue` KOs) on + the W0 generic ingest path. +- **Why:** Second real connector — proves the framework against a non-GitHub + OAuth + GraphQL shape and gives the run/chain loop a second real source. +- **Blocker to resolve first:** Linear has **no OAuth device-code flow** (the + affordance GitHub's `connect` UX is built on). Decide: localhost-redirect + OAuth listener vs. **personal access token** (`authType: "api-key"`). PAT is + the leaning for alpha speed. +- **Where to start:** `packages/federation/src/connectors/linear/` + (`connector.ts`, `api.ts` GraphQL client, `normalize.ts`); register in + `connectors/registry.ts`; CLI flag→params mapping in `cli/src/index.ts` + (`--team`, `--state`, `--label`, `--since`). Env `STACY_LINEAR_CLIENT_ID` or a + PAT entry in the keychain. +- **Depends on:** Phase 3 W0 (generic `ingestCommand`) landing first. + +## v0.3.1 — Real-API robustness (deferred from Phase 3 / W4) + +- **What:** Rate-limit backoff/retry + resumable ingest cursor. +- **Why:** Repeated dogfooding against real APIs hits 429s and transient + failures; today an interrupted `ingest` restarts from scratch. +- **Where to start:** centralize retry/backoff in `connectors/http.ts` (honor + `Retry-After` / `X-RateLimit-Reset`; `rate-limiter.ts` already models the + window). Add a per-(connector, source) cursor file under + `/connectors/` so `ingest` resumes. Optional: a provider-agnostic + second reference adapter in `scripts/` to prove the adapter contract. +- **Depends on:** best done once ≥2 connectors exist (after W2) so there is real + load to harden against. + +## Post-v0.2 — Independent validation gate (carryover) + +- **What:** Independent engineer validates the live GitHub + Anthropic paths + with their own credentials; meet design-partner metrics (15 alpha users, ≥10 + real ingests, ≥5 `agent_output` KOs). +- **Why:** `releases/v2026.602.0.md` is **Draft** until this clears; it also + gates tagging public v0.3. +- **Where:** `PHASE-2-HANDOFF.md` §4 test plan. diff --git a/cli/src/index.ts b/cli/src/index.ts index 4d9fb106..4675bd18 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -27,7 +27,7 @@ import { registerPluginCommands } from "./commands/client/plugin.js"; import { registerClientAuthCommands } from "./commands/client/auth.js"; import { cliVersion } from "./version.js"; import { applyStacyEnvAliases } from "./config/env-aliases.js"; -import { agentRunCommand, registerFederationCommands, runTaskCommand } from "@arpanstacy/stacy-federation/verbs"; +import { agentRunCommand, registerFederationCommands, runChainCommand, runTaskCommand } from "@arpanstacy/stacy-federation/verbs"; const program = new Command(); applyStacyEnvAliases(); @@ -148,6 +148,7 @@ program .option("--repair", "Attempt automatic repairs during doctor", true) .option("--no-repair", "Disable automatic repairs during doctor") .option("--use ", "Input Knowledge Object for an AI run; repeat for multiple", collectOption, []) + .option("--chain ", "Run a multi-step chain from a JSON spec file (steps[] with @stepId refs)") .option("--model ", "Model identifier passed to the run adapter") .option("--adapter ", "Run adapter to use: anthropic (default) or deterministic") .option("--input ", "Input file for a public federation demo task") @@ -164,6 +165,10 @@ program .option("--db-url ", "Database connection string") .option("--json", "Print raw JSON output", false) .action(async (task, opts) => { + if (typeof opts.chain === "string" && opts.chain.trim()) { + await runChainCommand({ ...opts, noCache: opts.cache === false }); + return; + } if (typeof task === "string" && task.trim()) { if (Array.isArray(opts.use) && opts.use.length > 0) { await agentRunCommand(task, { ...opts, noCache: opts.cache === false }); diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 4f87a44a..bd05f88e 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -36,14 +36,30 @@ Operations on locally stored signed Knowledge Objects (KOs). ### `stacy brain create` Create and store a local signed Knowledge Object. +Provide content one of four ways: inline JSON, a prompt, a single local file, or +a directory of files. The file/directory modes are credential-free. + | Option | Description | | --- | --- | | `--content-json ` | KO content as inline JSON. | | `--prompt ` | Generate KO content from a prompt. | +| `--file ` | Create a KO from a local text/markdown/json file (no credentials). Wraps it in a `{ kind: "document", source, mediaType, text\|data }` envelope. | +| `--dir ` | Create one KO per file under a directory (no credentials). | +| `--glob ` | Glob pattern for `--dir`, e.g. `'**/*.md'` (relative to `--dir` or cwd). | +| `--ext ` | Comma-separated extension allowlist for directory ingest, e.g. `md,txt,json`. | +| `--source-label