From b8cee68007370552751a11768998ed70ea558aaa Mon Sep 17 00:00:00 2001 From: Deniffer Date: Tue, 4 Aug 2026 12:32:55 +0800 Subject: [PATCH 1/4] docs: reorganize README for clarity and enhance installation instructions --- README.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a5e8914..a673d3f 100644 --- a/README.md +++ b/README.md @@ -6,46 +6,74 @@ has one CLI and one workspace package: `gkit`. The reviewed provider surface includes DataForSEO, PostHog, Google Ads, Google Search Console, and Bing Webmaster. -## Install +## Requirements + +gkit runs on [Bun](https://bun.sh/). Confirm that Bun is available before +installing: + +```bash +bun --version +``` -gkit requires [Bun](https://bun.sh/) and is distributed only as a public npm -tarball attached to GitHub Releases. It is not published to an npm registry. +gkit is distributed only as a public npm tarball attached to GitHub Releases. +It is not published to an npm registry, so the GitHub URL is required in every +install command. + +## Install Install the latest stable release globally: ```bash bun add --global "gkit@https://github.com/celados/gkit/releases/latest/download/gkit.tgz" -gkit --schema +gkit --schema gsc ``` +If the install succeeds but `gkit` is not found, run `bun pm bin --global` and +make sure the printed directory is included in your `PATH`. + Install an exact version instead: ```bash -bun add --global "gkit@https://github.com/celados/gkit/releases/download/v0.1.1/gkit-0.1.1.tgz" -gkit --schema +VERSION=0.1.1 +bun add --global "gkit@https://github.com/celados/gkit/releases/download/v${VERSION}/gkit-${VERSION}.tgz" ``` Prereleases are available only through their exact version URLs and never replace the stable `latest` download. -Upgrade to the newest stable release by running the latest install command -again. To uninstall: +Because the stable URL does not change between releases, remove the installed +copy before upgrading so Bun cannot reuse an older cached resolution: + +```bash +bun remove --global gkit +bun add --global "gkit@https://github.com/celados/gkit/releases/latest/download/gkit.tgz" +gkit --schema gsc +``` + +To uninstall: ```bash bun remove --global gkit ``` -## Discover capabilities +## Quick start + +### 1. Discover capabilities Discovery commands are offline and do not load a profile or resolve secrets: ```bash gkit --schema +gkit --schema gsc gkit docs --provider gsc -gkit describe --id gsc.search-analytics.query +gkit describe --id gsc.properties.list ``` -## Configure an App profile +Use `gkit --help` for the complete command shape. Use `describe` before calling +an operation: it returns the reviewed input schema, effects, examples, and +artifact behavior for that capability. + +### 2. Configure an App profile Provider execution must bind exactly one App profile. Create one JSON file at `$XDG_CONFIG_HOME/gkit/profiles/.json`, or at @@ -104,6 +132,42 @@ GKIT_PROFILE=my-app gkit posthog doctor or falls back to another App profile. Compare multiple Apps by running separate invocations and joining their outputs outside gkit. +### 3. Check the profile + +Run `doctor` before making a provider request. It checks the selected profile +and its provider configuration without printing secret values: + +```bash +gkit --profile my-app gsc doctor +``` + +### 4. Preview, then execute + +Start with the exact example returned by `describe` and keep `--dry-run` while +reviewing the request: + +```bash +gkit --profile my-app gsc api call \ + --operation-id gsc.properties.list \ + --input '{}' \ + --out ./gsc-properties-plan.json \ + --dry-run +``` + +Remove `--dry-run` only when the profile and request are correct: + +```bash +gkit --profile my-app gsc api call \ + --operation-id gsc.properties.list \ + --input '{}' \ + --out ./gsc-properties.json +``` + +Artifacts use no-replace behavior by default. Choose a new output path for a +later run, or add `--force` only after reviewing the existing destination. +DataForSEO operations that can spend money additionally require both +`--allow-spend` and an explicit `--max-spend-usd` limit. + ## Configure an Agent Agents do not need provider-specific CLIs or their own copies of credentials. From 8eb8792e89282039d2991c899a7aa57ab40e2d7a Mon Sep 17 00:00:00 2001 From: Deniffer Date: Fri, 14 Aug 2026 14:40:00 +0800 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20define=20the=20minimal=20growth=20w?= =?UTF-8?q?orkspace=20product=20boundary=20=E2=80=94=20preserve=20confirme?= =?UTF-8?q?d=20decisions=20without=20expanding=20the=20first=20slice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the product workspace the continuity root, keep growth projects optional, and use portable OKF Markdown entered through GROWTH.md. Record the app_profile selector as a fail-closed execution prerequisite while deferring Gesso, connectors, databases, realtime collaboration, and autonomous orchestration. --- AGENTS.md | 68 +++++++ CONTEXT.md | 77 ++++++++ ...-headless-agent-native-growth-workspace.md | 16 ++ ...-an-operation-journal-for-runtime-facts.md | 8 + ...d-live-operations-as-append-only-events.md | 8 + ...ublic-cli-without-a-compatibility-layer.md | 8 + ...-behind-purpose-built-agent-projections.md | 10 ++ ...rovider-modules-behind-one-runtime-seam.md | 10 ++ ...cript-for-the-growth-capability-runtime.md | 10 ++ ...tion-identity-from-capability-selection.md | 8 + ...vider-uncertainty-at-the-module-handoff.md | 10 ++ ...ine-and-repair-only-a-torn-journal-tail.md | 8 + ...live-execution-in-operation-projections.md | 8 + ...states-and-require-evidence-for-success.md | 10 ++ ...operations-and-do-not-reconcile-history.md | 8 + ...on-event-vocabulary-minimal-and-ordered.md | 10 ++ ...eration-journal-as-private-locked-jsonl.md | 10 ++ ...ly-validate-non-secret-operation-events.md | 10 ++ ...project-activity-from-growth-operations.md | 8 + ...oject-memory-from-durable-project-facts.md | 8 + ...-orchestration-in-external-agent-skills.md | 8 + ...start-the-project-store-as-local-sqlite.md | 8 + ...cally-and-associate-projects-explicitly.md | 8 + ...markdown-for-the-first-growth-documents.md | 8 + ...growth-workspaces-portable-across-hosts.md | 8 + ...e-growth-md-as-the-workspace-entrypoint.md | 8 + ...ost-document-tools-instead-of-gkit-crud.md | 8 + ...e-one-app-profile-selector-in-growth-md.md | 13 ++ docs/gkit-product-architecture-context.md | 167 ++++++++++++++++++ 29 files changed, 549 insertions(+) create mode 100644 AGENTS.md create mode 100644 CONTEXT.md create mode 100644 docs/adr/0001-gkit-is-a-headless-agent-native-growth-workspace.md create mode 100644 docs/adr/0002-use-an-operation-journal-for-runtime-facts.md create mode 100644 docs/adr/0003-record-live-operations-as-append-only-events.md create mode 100644 docs/adr/0004-replace-the-public-cli-without-a-compatibility-layer.md create mode 100644 docs/adr/0005-share-one-runtime-behind-purpose-built-agent-projections.md create mode 100644 docs/adr/0006-allow-polyglot-provider-modules-behind-one-runtime-seam.md create mode 100644 docs/adr/0007-keep-typescript-for-the-growth-capability-runtime.md create mode 100644 docs/adr/0008-separate-operation-identity-from-capability-selection.md create mode 100644 docs/adr/0009-start-provider-uncertainty-at-the-module-handoff.md create mode 100644 docs/adr/0010-quarantine-and-repair-only-a-torn-journal-tail.md create mode 100644 docs/adr/0011-do-not-infer-live-execution-in-operation-projections.md create mode 100644 docs/adr/0012-use-closed-operation-states-and-require-evidence-for-success.md create mode 100644 docs/adr/0013-retries-create-new-operations-and-do-not-reconcile-history.md create mode 100644 docs/adr/0014-keep-the-first-operation-event-vocabulary-minimal-and-ordered.md create mode 100644 docs/adr/0015-store-the-operation-journal-as-private-locked-jsonl.md create mode 100644 docs/adr/0016-version-and-strictly-validate-non-secret-operation-events.md create mode 100644 docs/adr/0017-separate-project-activity-from-growth-operations.md create mode 100644 docs/adr/0018-derive-project-memory-from-durable-project-facts.md create mode 100644 docs/adr/0019-keep-sop-orchestration-in-external-agent-skills.md create mode 100644 docs/adr/0020-start-the-project-store-as-local-sqlite.md create mode 100644 docs/adr/0021-ingest-source-facts-deterministically-and-associate-projects-explicitly.md create mode 100644 docs/adr/0022-use-okf-markdown-for-the-first-growth-documents.md create mode 100644 docs/adr/0023-keep-growth-workspaces-portable-across-hosts.md create mode 100644 docs/adr/0024-use-growth-md-as-the-workspace-entrypoint.md create mode 100644 docs/adr/0025-use-host-document-tools-instead-of-gkit-crud.md create mode 100644 docs/adr/0026-declare-one-app-profile-selector-in-growth-md.md create mode 100644 docs/gkit-product-architecture-context.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..79190bc --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,68 @@ +# gkit Agent Instructions + +## Product north star + +gkit is a **headless, agent-native Growth Workspace**: a durable place where an +External Agent can find prior growth work, use bounded growth capabilities, and +leave trustworthy work for a later Agent to continue. + +The continuity root is the Product's Growth Workspace, not an Agent session or a +Growth Project. A Growth Project is optional organization inside the Workspace. + +## First product contract + +- A portable Growth Workspace is a directory with one root `GROWTH.md` OKF file. +- `GROWTH.md` uses `type: GrowthWorkspace`, identifies the Product in + human-readable form, declares one non-secret `app_profile` selector, and links + to the important Growth Documents. +- Growth Documents are ordinary OKF Markdown files. Their contents are the source + of truth. +- A fresh External Agent starts with `ctx read .`, reads `./GROWTH.md`, follows + the relevant links, and can continue the work without relying on chat history. +- Agents edit documents with the relevant OKF/document skill and host file tools. + gkit does not own Workspace or Document CRUD. +- Documents cite durable provider artifacts or other inspectable evidence and + state the boundary between observed facts and Agent interpretation. + +The same logical Workspace must remain portable between local and future Gesso +hosts. A host supplies storage, `ctx`, file editing, execution, and secrets; it +does not redefine Growth Workspace semantics. + +The `growth-workspace` Agent skill is installed by the host, not copied into each +Growth Workspace. A Workspace remains portable data: `GROWTH.md`, Growth +Documents, requests, and referenced artifacts. + +## Existing execution boundary + +The repository currently implements an agent-first CLI and provider capability +runtime, plus a small project-local `growth-workspace` Agent skill that composes +the first document workflow. It does not yet implement the complete Growth +Workspace product. + +- External Agents and Growth Skills own goals, orchestration, interpretation, + and next-step judgment. +- The runtime owns capability discovery, profile scope, effects, authorization, + provider dispatch, cost, artifacts, and explicit uncertain outcomes. +- One live operation binds one explicit App Profile. Cross-provider composition + belongs to the External Agent. +- The Workspace supplies the App Profile selector; the host resolves its local + profile and secrets. If the selector is absent, stop before provider doctor or + dispatch. Never scan host profiles or infer one from the Product title. +- Discovery remains offline and must not resolve secrets. Live execution stays + behind the existing capability and provider seams. +- Provider facts and artifacts are not conclusions. Agents must preserve their + provenance and interpretation limits in Growth Documents. + +## Complexity guardrail + +For the first asynchronous vertical slice, do not add a document database, +private format, Workspace CRUD API, manifest, UUID, registry, multi-profile mapping, +host adapter, autonomous planner, workflow engine, CRDT, presence, comments, +permissions, cloud infrastructure, GitHub Connector, or Gesso integration. + +Older accepted ADRs about Project Activity, Project Memory, Source Facts, and +connectors remain constraints if those features are revisited; they are not +current implementation scope and must not be pulled into the first slice. + +Use [CONTEXT.md](./CONTEXT.md) for current canonical language and +[docs/adr](./docs/adr) for accepted architectural decisions. diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..40a6257 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,77 @@ +# gkit + +gkit is a headless, agent-native Growth Workspace where External Agents continue +durable growth work for a Product across sessions and Agent hosts. + +Only language required by the current product boundary appears here. Earlier +ADRs preserve accepted constraints for deferred designs; their larger taxonomies +are not part of the first vertical slice. + +## Workspace + +**Product(产品)**: +The durable business subject being grown and the continuity root of one Growth +Workspace. +_Avoid_: Growth Project, repository, App Profile + +**Growth Workspace(增长工作空间)**: +The portable Product workspace in which External Agents find prior work, use +bounded growth capabilities, and leave durable work for later Agents. Its first +physical form is a directory identified by `GROWTH.md`. +_Avoid_: Agent session, provider CLI, autonomous Growth Agent + +**App Profile Selector(应用配置选择器)**: +The non-secret App Profile name declared by a Growth Workspace so a host can +bind provider execution to exactly one Product identity. It names host-local +configuration but contains no credential, path, or provider mapping. +_Avoid_: Product, profile map, secret reference + +**Growth Document(增长文档)**: +An addressable OKF Markdown file whose contents preserve growth work across Agent +sessions and hosts. It may hold an investigation, plan, analysis, strategy, or +handoff without requiring a separate native object type. +_Avoid_: chat summary, database record, fixed Investigation type + +**Growth Project(增长项目)**: +Optional organization for a bounded initiative inside a Growth Workspace. It is +not required for work to belong to the Product or survive Agent sessions. +_Avoid_: Workspace root, App Profile, Agent session + +**External Agent(外部 Agent)**: +The reasoning actor that discovers and edits Growth Documents, selects and +composes Growth Capabilities, interprets evidence, and chooses next steps. +_Avoid_: built-in gkit planner, Provider Module + +**Growth Skill(增长技能)**: +An Agent-consumed procedure for performing repeatable growth work with Growth +Documents and Growth Capabilities. It owns neither credentials nor provider +execution. +_Avoid_: Growth Capability, workflow engine, provider integration + +## Evidence and execution + +**Evidence(增长证据)**: +A durable, attributable observation whose source can be inspected. Evidence may +support an Agent's interpretation, but it is not itself a conclusion. +_Avoid_: Insight, unsupported claim, success message + +**Growth Capability(增长能力)**: +A bounded provider or deterministic operation with one growth-domain intent and +an independently inspectable result. +_Avoid_: provider endpoint, Growth Skill, business goal + +**Growth Capability Runtime(增长能力运行时)**: +The deterministic execution module used by External Agents. It owns discovery, +profile scope, policy, dispatch, cost, artifacts, and explicit uncertain outcomes, +but not goals or interpretation. +_Avoid_: complete Growth Workspace, autonomous Agent, generic integration layer + +**Growth Operation(增长操作)**: +One execution of a Growth Capability whose scope, dispatch, outcome, cost, and +resulting evidence can be audited. +_Avoid_: Growth Project, business experiment, provider endpoint + +**Provider Module**: +A purpose-built implementation of one provider's native capabilities behind the +runtime seam. +_Avoid_: plugin framework, Growth Skill, universal connector diff --git a/docs/adr/0001-gkit-is-a-headless-agent-native-growth-workspace.md b/docs/adr/0001-gkit-is-a-headless-agent-native-growth-workspace.md new file mode 100644 index 0000000..8af0666 --- /dev/null +++ b/docs/adr/0001-gkit-is-a-headless-agent-native-growth-workspace.md @@ -0,0 +1,16 @@ +--- +type: ADR +status: accepted +--- + +# gkit is a headless, agent-native Growth Workspace + +gkit is the durable Growth Workspace for one Product. Its first product contract +is a portable directory entered through `GROWTH.md`, with authoritative OKF +Markdown Growth Documents and inspectable Evidence. A Growth Project is optional +organization inside the Workspace, not the continuity root. External Agents and +Growth Skills own goals, orchestration, interpretation, and next-step judgment; +the existing Growth Capability Runtime remains the bounded execution module. +We chose this over a thin provider CLI because Agents need cross-session +continuity, and over an autonomous Growth Agent because reasoning belongs to the +Agent host. diff --git a/docs/adr/0002-use-an-operation-journal-for-runtime-facts.md b/docs/adr/0002-use-an-operation-journal-for-runtime-facts.md new file mode 100644 index 0000000..f4cee6c --- /dev/null +++ b/docs/adr/0002-use-an-operation-journal-for-runtime-facts.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Use an Operation Journal for runtime facts, not Workspace memory + +Every Growth Operation is recorded in one local durable Operation Journal that links its App Profile, capability, effects, provider attempt, outcome, cost, and Evidence. The Journal is the Growth Capability Runtime's execution fact source; Growth Project history records separate attributed Project Activities that may reference Operations and Evidence, while Project Memory is derived from those business facts. We chose this over a stateless CLI because Agents must recover and verify prior operations, and reject treating the Journal as the entire Workspace model because low-level provider calls cannot explain why a Growth Project advanced. diff --git a/docs/adr/0003-record-live-operations-as-append-only-events.md b/docs/adr/0003-record-live-operations-as-append-only-events.md new file mode 100644 index 0000000..0f57310 --- /dev/null +++ b/docs/adr/0003-record-live-operations-as-append-only-events.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Record live Growth Operations as append-only events + +The Operation Journal begins a Growth Operation after an Agent Projection has syntactically recognized a provider execution request and before dereferencing or semantically validating its input, App Profile, capability, or effects. Unrecognized projection syntax, discovery, doctor, and dry-run activity are not Growth Operations and stay outside the Journal. The initial event records only non-sensitive selectors, input references, and an input digest when one can be computed safely; immutable Operation Events continue through settlement or reconciliation so execution history cannot be rewritten by Project Activity, Project Memory, or a later Agent. diff --git a/docs/adr/0004-replace-the-public-cli-without-a-compatibility-layer.md b/docs/adr/0004-replace-the-public-cli-without-a-compatibility-layer.md new file mode 100644 index 0000000..6fe4499 --- /dev/null +++ b/docs/adr/0004-replace-the-public-cli-without-a-compatibility-layer.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Replace the first Agent Projection without a compatibility layer + +The CLI is the first Agent Projection of the headless Growth Workspace and may be redesigned rather than preserve historical command shapes. gkit currently has one direct consumer, so each new tracer-bullet surface will be proven with behavioural evidence before an atomic hard cut removes the old surface, aliases, and implementations. The PostHog tracer bullet replaces `posthog api call --operation-id posthog.query.run` with the provider-owned `posthog query hogql` command and does not retain an alias; long-lived dual interfaces would obscure which Workspace contract Agents should learn. diff --git a/docs/adr/0005-share-one-runtime-behind-purpose-built-agent-projections.md b/docs/adr/0005-share-one-runtime-behind-purpose-built-agent-projections.md new file mode 100644 index 0000000..66db386 --- /dev/null +++ b/docs/adr/0005-share-one-runtime-behind-purpose-built-agent-projections.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Share one Workspace and Runtime behind purpose-built Agent Projections + +The CLI is the first projection of the shared Growth Workspace and Growth Capability Runtime; a future MCP or human interface may adapt the same modules but may not reimplement Project, policy, or provider rules. Capability discovery and operation inspection are shared, while execution remains purpose-built around each provider's native domain instead of a public `run(id, unknown)` interface. Provider execution returns a compact Operation Projection; `operations get`, `operations events`, and bounded `operations list` expose inspection and crash recovery, while immutable Operation Events remain a diagnostic surface rather than the default result. + +Execution exits zero only for `succeeded`, one for other durable outcomes, and 130 after a gracefully recorded interruption; inspection exits zero whenever the requested durable facts were read successfully, regardless of Operation State. Stdout contains one structured Agent Projection and diagnostics remain on stderr. This preserves one business model across headless surfaces without flattening provider semantics or creating parallel implementations. diff --git a/docs/adr/0006-allow-polyglot-provider-modules-behind-one-runtime-seam.md b/docs/adr/0006-allow-polyglot-provider-modules-behind-one-runtime-seam.md new file mode 100644 index 0000000..114c535 --- /dev/null +++ b/docs/adr/0006-allow-polyglot-provider-modules-behind-one-runtime-seam.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Allow polyglot Provider Modules behind one runtime seam + +The Growth Capability Runtime defines one provider execution seam while allowing Provider Modules to run either in-process or through a controlled subprocess adapter. The seam standardizes cancellation, timeout, credential exposure, dispatch outcome, artifacts, and Operation Events, but leaves provider APIs, SDKs, pagination, and language choices inside each module. Provider Modules return Evidence to the Workspace through this seam and never own Project Activity or Project Memory. + +This avoids coupling the runtime language decision to a simultaneous rewrite of every provider, avoids permanently forcing all providers through process startup when an in-process adapter earns its place, and keeps provider variation local without coupling the Workspace model to a language or SDK. diff --git a/docs/adr/0007-keep-typescript-for-the-growth-capability-runtime.md b/docs/adr/0007-keep-typescript-for-the-growth-capability-runtime.md new file mode 100644 index 0000000..29daa6c --- /dev/null +++ b/docs/adr/0007-keep-typescript-for-the-growth-capability-runtime.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Keep TypeScript for the Growth Capability Runtime + +The Growth Capability Runtime remains a TypeScript module inside the headless Growth Workspace while adopting the event ordering, durable provider checkpoint, projection reducer, and recovery semantics proven on branch `prototype/rust-operation-runtime` at commit `9a75124`. The prototype showed that the reliability came from architecture rather than Rust; the existing TypeScript code already has the required durable-file, artifact, cancellation, validation, and adapter primitives, while a Rust core would add native distribution and cross-process integration costs without increasing leverage for Agent Projections, Workspace modules, or Provider Modules. + +This decision does not require every future Workspace module or polyglot Provider Module to use TypeScript. Revisit the runtime language only if the TypeScript tracer bullet exposes a concrete failure the architecture itself cannot remove. diff --git a/docs/adr/0008-separate-operation-identity-from-capability-selection.md b/docs/adr/0008-separate-operation-identity-from-capability-selection.md new file mode 100644 index 0000000..59bf644 --- /dev/null +++ b/docs/adr/0008-separate-operation-identity-from-capability-selection.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Separate Growth Operation identity from Capability selection + +Every accepted Live Operation Intent receives a runtime-generated, single-use Operation ID, while a separate Capability ID selects what the Provider Module will execute. Project Activities, Playbook Runs, Growth Actions, and provider request IDs remain separate identities and may reference the Operation without becoming it. The current CLI replaces capability selection through `--operation-id` with `--capability-id`; runtime-owned identity gives every execution one authoritative history and prevents business-level Workspace objects from being overloaded with provider lifecycle semantics. diff --git a/docs/adr/0009-start-provider-uncertainty-at-the-module-handoff.md b/docs/adr/0009-start-provider-uncertainty-at-the-module-handoff.md new file mode 100644 index 0000000..68c4da5 --- /dev/null +++ b/docs/adr/0009-start-provider-uncertainty-at-the-module-handoff.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Start provider uncertainty at the Provider Module handoff + +The Growth Capability Runtime durably records `provider_attempt_started` after all local preparation and immediately before calling the Provider Module. This checkpoint means execution ownership crossed the runtime seam, not that a network request is confirmed; after it exists, only an explicit Provider Module result may establish `not_dispatched`, while interruption or loss of the result projects to `unknown`. Cancellation before the checkpoint records `not_dispatched`; cancellation after it is forwarded to the Provider Module, whose explicit result wins, and otherwise remains `unknown` even after a hard exit. + +Neither a Growth Skill, Project Activity, Project Memory, nor a later Agent may reinterpret that uncertainty, because business context and a runtime AbortSignal cannot prove what an in-process or subprocess Provider Module did after accepting control. diff --git a/docs/adr/0010-quarantine-and-repair-only-a-torn-journal-tail.md b/docs/adr/0010-quarantine-and-repair-only-a-torn-journal-tail.md new file mode 100644 index 0000000..16879d6 --- /dev/null +++ b/docs/adr/0010-quarantine-and-repair-only-a-torn-journal-tail.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Quarantine and repair only a torn Journal tail + +Under the exclusive Journal writer lock, the Growth Capability Runtime may recover an incomplete final JSONL fragment by preserving its exact bytes in a recovery sidecar and truncating only that fragment before the next append. The Agent Projection exposes this as Journal Health beside the requested Operation Projection rather than changing the Operation, its linked Project Activity, or Project Memory. A complete invalid event, interior corruption, or illegal event order blocks Journal reads and writes instead of being guessed or discarded, so Workspace-level projections cannot silently inherit invented execution facts. diff --git a/docs/adr/0011-do-not-infer-live-execution-in-operation-projections.md b/docs/adr/0011-do-not-infer-live-execution-in-operation-projections.md new file mode 100644 index 0000000..218ea93 --- /dev/null +++ b/docs/adr/0011-do-not-infer-live-execution-in-operation-projections.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Do not infer live execution in Operation Projections + +An Operation Projection reports only facts derivable from durable Operation Events and does not claim that a provider attempt is `running` from local process state, elapsed time, a PID, a lock, an Agent session, or Project Activity. A `provider_attempt_started` checkpoint without a durable outcome exposes provider outcome as unknown whether the original process is still executing or has exited. Accurate asynchronous status would require explicit lease or heartbeat facts and must not be inferred by a future Workspace or Memory projection. diff --git a/docs/adr/0012-use-closed-operation-states-and-require-evidence-for-success.md b/docs/adr/0012-use-closed-operation-states-and-require-evidence-for-success.md new file mode 100644 index 0000000..966094d --- /dev/null +++ b/docs/adr/0012-use-closed-operation-states-and-require-evidence-for-success.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Use closed Operation States and require Evidence for success + +An Operation Projection is one of `received`, `not_dispatched`, `succeeded`, `failed`, or `unknown`, represented as a closed variant rather than independent status flags. `succeeded` requires provider success and the required Evidence or artifact receipt to be durable; provider success followed by Evidence publication failure is `failed` with `providerOutcome: confirmed_success`, so neither an Agent nor a Growth Skill can infer that retry is safe. A Provider Attempt without a durable result is `unknown`, while an Operation that has not crossed that checkpoint remains `received` until an explicit non-dispatched outcome is recorded. + +These execution states remain distinct from Project Stage, Playbook Run progress, Growth Action outcome, and any interpretation recorded as an Insight. diff --git a/docs/adr/0013-retries-create-new-operations-and-do-not-reconcile-history.md b/docs/adr/0013-retries-create-new-operations-and-do-not-reconcile-history.md new file mode 100644 index 0000000..c847856 --- /dev/null +++ b/docs/adr/0013-retries-create-new-operations-and-do-not-reconcile-history.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Retries create new Operations and do not reconcile history + +Every retry creates a new runtime-generated Operation ID and may reference the prior operation through `retryOfOperationId`; it never appends execution events to, reuses, or replaces the prior identity. An unknown Operation changes only through an evidence-backed reconciliation event about that same Provider Attempt, and a later successful retry is not such Evidence. A Project Activity may explain why an Agent retried and a Growth Action may reference both Operations, but neither may rewrite their histories. The first PostHog tracer bullet permits unknown outcomes to remain unresolved and does not add a manual reconciliation command. diff --git a/docs/adr/0014-keep-the-first-operation-event-vocabulary-minimal-and-ordered.md b/docs/adr/0014-keep-the-first-operation-event-vocabulary-minimal-and-ordered.md new file mode 100644 index 0000000..f6dfe97 --- /dev/null +++ b/docs/adr/0014-keep-the-first-operation-event-vocabulary-minimal-and-ordered.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Keep the first Operation Event vocabulary minimal and ordered + +The first runtime tracer bullet uses only `operation_started`, `operation_not_dispatched`, `provider_attempt_started`, `artifact_recorded`, `operation_succeeded`, `operation_failed`, and `provider_outcome_unknown`. One Growth Operation crosses the Provider Module seam at most once; module-internal transport retries remain provider semantics, and a caller retry creates another Operation. The reducer rejects histories that do not start exactly once, cross the seam more than once, record runtime non-dispatch after the checkpoint, record provider outcomes before it, succeed without prior provider-response Evidence, or append execution events after a settled outcome. Journal order is authoritative and timestamps are descriptive only; complete invalid JSON, duplicate event identity, unknown schema versions, and illegal ordering block reads and writes rather than producing a best-effort projection. + +Project events such as Hypotheses, Insights, Decisions, Growth Actions, and Playbook Runs do not enter this vocabulary; they belong to Project Activity and link to Operations or Evidence by identity. diff --git a/docs/adr/0015-store-the-operation-journal-as-private-locked-jsonl.md b/docs/adr/0015-store-the-operation-journal-as-private-locked-jsonl.md new file mode 100644 index 0000000..7d4999c --- /dev/null +++ b/docs/adr/0015-store-the-operation-journal-as-private-locked-jsonl.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Store the Operation Journal as private locked JSONL + +The local Operation Journal lives at `$XDG_STATE_HOME/gkit/operations.jsonl`, or `~/.local/state/gkit/operations.jsonl` when the XDG root is unset, under a `0700` directory with `0600` Journal, lock, and recovery files. Reads and appends use the same short-lived exclusive ownership-token lock; each canonical JSON line is fully written and file-synced, with the parent directory synced when storage is created. A stale lock is quarantined only when its recorded PID is definitively absent, while a live or uncertain owner blocks access. + +JSONL is an initial implementation choice for local runtime facts, not the storage model for the entire Growth Workspace or a requirement that Project Activity and Project Memory share the same file. diff --git a/docs/adr/0016-version-and-strictly-validate-non-secret-operation-events.md b/docs/adr/0016-version-and-strictly-validate-non-secret-operation-events.md new file mode 100644 index 0000000..a53f0e4 --- /dev/null +++ b/docs/adr/0016-version-and-strictly-validate-non-secret-operation-events.md @@ -0,0 +1,10 @@ +--- +type: ADR +status: accepted +--- + +# Version and strictly validate non-secret Operation Events + +Every Operation Event has an exact versioned envelope containing `schemaVersion`, unique `eventId`, runtime-generated `operationId`, `eventType`, RFC 3339 `occurredAt`, and a strictly validated payload. Unknown versions or keys fail closed; Journal order, not identifiers or timestamps, determines execution fact order. Initial intent events retain only non-sensitive selectors and input-source references, while validated input digests, resolved capability, and effects appear at the Provider Attempt checkpoint. + +Raw or unvalidated input, credentials, authorization material, unresolved provider details, and provider bytes never enter the Journal, Project Activity, or Project Memory; provider Evidence is published through the secret-scanning artifact module, persisted errors use stable codes with redacted messages, and Workspace records reference Evidence through non-secret identity and provenance. diff --git a/docs/adr/0017-separate-project-activity-from-growth-operations.md b/docs/adr/0017-separate-project-activity-from-growth-operations.md new file mode 100644 index 0000000..de7220b --- /dev/null +++ b/docs/adr/0017-separate-project-activity-from-growth-operations.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Separate Project Activity from Growth Operations + +Growth Project history records attributed Objectives, Hypotheses, Evidence, Insights, Decisions, Growth Actions, and Playbook Run outcomes as Project Activity, while the Operation Journal records provider execution facts. A Project Activity may reference many Growth Operations, and one Operation may supply Evidence to a larger investigation, but Provider Modules cannot directly update Project Stage or author Insights and Decisions. We chose this separation so business history explains why the project advanced without polluting Project Memory with low-level calls or allowing provider responses to masquerade as conclusions. diff --git a/docs/adr/0018-derive-project-memory-from-durable-project-facts.md b/docs/adr/0018-derive-project-memory-from-durable-project-facts.md new file mode 100644 index 0000000..59bd080 --- /dev/null +++ b/docs/adr/0018-derive-project-memory-from-durable-project-facts.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Derive Project Memory from durable Project facts + +Project Memory is a regenerable orientation projection derived from attributed Project Activity and linked Evidence, not an independently editable summary or source of truth. Important memory claims must remain traceable to durable Objectives, Hypotheses, Insights, Decisions, Growth Actions, outcomes, or Evidence; exact provider facts are read from their Evidence and Operations when needed. We chose this over chat-history memory because Agents and sessions change, and over treating every event as prompt context because orientation requires compression without sacrificing provenance. diff --git a/docs/adr/0019-keep-sop-orchestration-in-external-agent-skills.md b/docs/adr/0019-keep-sop-orchestration-in-external-agent-skills.md new file mode 100644 index 0000000..2152a97 --- /dev/null +++ b/docs/adr/0019-keep-sop-orchestration-in-external-agent-skills.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Keep SOP orchestration in External Agent Skills + +Growth Skills guide an External Agent through a growth SOP by reading Project Context, selecting and composing Growth Capabilities, interpreting Evidence, and proposing attributed Project Activity. gkit does not embed an LLM planner or let Skills bypass App Profile scope, provider execution, authorization, or Evidence rules; the Agent host may change without changing Workspace facts. This keeps non-deterministic reasoning outside the deterministic Capability Runtime while allowing provider integrations and SOPs to evolve independently. diff --git a/docs/adr/0020-start-the-project-store-as-local-sqlite.md b/docs/adr/0020-start-the-project-store-as-local-sqlite.md new file mode 100644 index 0000000..93dd3fb --- /dev/null +++ b/docs/adr/0020-start-the-project-store-as-local-sqlite.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: superseded by ADR-0022 +--- + +# Start the Project Store as local SQLite + +The first authoritative Project Store is one local SQLite database owned by the Growth Workspace. Agent Projections and a future Gesso host must use the same Workspace module rather than create separate project stores; remote, replicated, or multi-writer storage waits for a demonstrated cross-machine continuity requirement. We chose this over JSONL because Project facts need indexed relationships and derived views, and over a cloud database because the first product has one operator and does not yet earn hosted identity, synchronization, or tenancy complexity. diff --git a/docs/adr/0021-ingest-source-facts-deterministically-and-associate-projects-explicitly.md b/docs/adr/0021-ingest-source-facts-deterministically-and-associate-projects-explicitly.md new file mode 100644 index 0000000..2228b27 --- /dev/null +++ b/docs/adr/0021-ingest-source-facts-deterministically-and-associate-projects-explicitly.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Ingest Source Facts deterministically and associate Projects explicitly + +Fact Connectors asynchronously verify, normalize, and deduplicate external deliveries into immutable Product-scoped Source Facts; mutable current projections are derived separately, and ordinary fact ingestion does not require an Agent run. A GitHub pull request becomes associated with a Growth Project only through an explicit Project label, while unlabeled repository activity remains Product context rather than being guessed into a Project. We chose this over Agent-authored upserts because reliable synchronization must not depend on probabilistic interpretation, and over automatic semantic association because a plausible but wrong Project link would corrupt durable context. diff --git a/docs/adr/0022-use-okf-markdown-for-the-first-growth-documents.md b/docs/adr/0022-use-okf-markdown-for-the-first-growth-documents.md new file mode 100644 index 0000000..db82f62 --- /dev/null +++ b/docs/adr/0022-use-okf-markdown-for-the-first-growth-documents.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Use OKF Markdown for the first Growth Documents + +The first Growth Documents are ordinary OKF Markdown files and their file contents are authoritative. External Agents use normal filesystem and document tools to find, read, and edit them; gkit does not introduce a document database, private document format, or revision service for the initial asynchronous collaboration slice. This supersedes ADR-0020's SQLite-first direction because Product Workspace continuity no longer depends on a Project Store. diff --git a/docs/adr/0023-keep-growth-workspaces-portable-across-hosts.md b/docs/adr/0023-keep-growth-workspaces-portable-across-hosts.md new file mode 100644 index 0000000..c1f61c6 --- /dev/null +++ b/docs/adr/0023-keep-growth-workspaces-portable-across-hosts.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Keep Growth Workspaces portable across hosts + +gkit owns the logical Growth Workspace for a Product, while the environment that stores and exposes it is a host. The first physical form is a portable directory of authoritative OKF Markdown Growth Documents and referenced artifacts that can stand alone or live inside an existing workspace; local filesystems and a future Gesso environment are hosts rather than dependencies of the Growth domain. We chose this boundary so gkit is not coupled to the current celados workspace, Lore, `ctx`, or Gesso infrastructure, while preserving one product model across local and managed execution. diff --git a/docs/adr/0024-use-growth-md-as-the-workspace-entrypoint.md b/docs/adr/0024-use-growth-md-as-the-workspace-entrypoint.md new file mode 100644 index 0000000..df34de1 --- /dev/null +++ b/docs/adr/0024-use-growth-md-as-the-workspace-entrypoint.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Use GROWTH.md as the Workspace entrypoint + +A portable Growth Workspace is identified by one `GROWTH.md` OKF document at its root. Its frontmatter declares `type: GrowthWorkspace` and human-readable Product context, while its structural Markdown body orients External Agents and links to important Growth Documents. The directory is the initial physical boundary; stable UUIDs, a separate manifest, registry entries, and profile mappings wait for demonstrated identity or synchronization conflicts. diff --git a/docs/adr/0025-use-host-document-tools-instead-of-gkit-crud.md b/docs/adr/0025-use-host-document-tools-instead-of-gkit-crud.md new file mode 100644 index 0000000..4d18fd6 --- /dev/null +++ b/docs/adr/0025-use-host-document-tools-instead-of-gkit-crud.md @@ -0,0 +1,8 @@ +--- +type: ADR +status: accepted +--- + +# Use host document tools instead of gkit CRUD + +External Agents discover and read `GROWTH.md` and Growth Documents through the host-provided `ctx` CLI, and create or edit OKF Markdown through Agent skills plus the host's file tools. Both the local and future Gesso hosts must supply these document capabilities; gkit will not add mandatory Workspace or Document CRUD commands. This keeps one portable document contract without duplicating host infrastructure or coupling Growth Documents to the gkit CLI. diff --git a/docs/adr/0026-declare-one-app-profile-selector-in-growth-md.md b/docs/adr/0026-declare-one-app-profile-selector-in-growth-md.md new file mode 100644 index 0000000..e02b40d --- /dev/null +++ b/docs/adr/0026-declare-one-app-profile-selector-in-growth-md.md @@ -0,0 +1,13 @@ +--- +type: ADR +status: accepted +--- + +# Declare one App Profile selector in GROWTH.md + +A Growth Workspace declares one non-secret `app_profile` selector in +`GROWTH.md`, and an External Agent passes that selector explicitly to gkit for +provider readiness and execution. The host owns the matching profile document +and secrets; neither gkit nor the Agent scans available profiles or guesses from +the Product name. This keeps the Workspace portable while making cold-start +execution deterministic and fail-closed. diff --git a/docs/gkit-product-architecture-context.md b/docs/gkit-product-architecture-context.md new file mode 100644 index 0000000..1a5d290 --- /dev/null +++ b/docs/gkit-product-architecture-context.md @@ -0,0 +1,167 @@ +--- +type: Reference +title: gkit Product Architecture Context +description: The confirmed minimum product boundary and first vertical slice for gkit. +status: active +version: 0.4 +generated: { by: codex/gpt-5, at: "2026-08-14T14:39:07+08:00" } +tags: [gkit, growth-workspace, agent-native] +--- + +# gkit Product Architecture Context + +## Product boundary + +gkit is the complete **headless, agent-native Growth Workspace**, analogous to +Macro in product shape: External Agents enter one durable workspace, find prior +work, use shared capabilities, and leave addressable work for later Agents. + +The Product's Growth Workspace is the continuity root. Growth Projects are +optional organization inside it. gkit is not an autonomous Growth Agent, a +generic connector platform, or only a provider CLI. + +The existing Growth Capability Runtime is a subordinate execution module. +External Agents and Growth Skills retain goals, orchestration, interpretation, +and next-step judgment. + +## Confirmed minimum + +1. **The Workspace is portable.** Its first physical form is a directory that + can live independently or inside a local or future Gesso host. See + [ADR-0023](./adr/0023-keep-growth-workspaces-portable-across-hosts.md). +2. **`GROWTH.md` is the entrypoint.** One root OKF document with + `type: GrowthWorkspace` gives human-readable Product identity, orientation, + and links. The directory is the first identity boundary. See + [ADR-0024](./adr/0024-use-growth-md-as-the-workspace-entrypoint.md). +3. **One App Profile selector binds execution.** `GROWTH.md` declares one + non-secret `app_profile` selector. The Agent passes it explicitly to gkit, + while the host resolves the matching profile and secrets. A missing selector + stops provider readiness and dispatch without profile scanning or guessing. + See + [ADR-0026](./adr/0026-declare-one-app-profile-selector-in-growth-md.md). +4. **Growth Documents are the first collaboration object.** They are ordinary + OKF Markdown files whose contents are authoritative. Investigation, plan, + analysis, and strategy begin as document patterns, not separate object types. + See [ADR-0022](./adr/0022-use-okf-markdown-for-the-first-growth-documents.md). +5. **Hosts provide document access.** Local and future Gesso hosts expose `ctx` + for discovery and reading. Agents edit through OKF/document skills and host + file tools; gkit adds no mandatory Workspace or Document CRUD. See + [ADR-0025](./adr/0025-use-host-document-tools-instead-of-gkit-crud.md). +6. **Collaboration is asynchronous.** A fresh Agent in another session or host + must be able to find, understand, and continue prior work without chat history. +7. **Reasoning stays external.** gkit does not embed a planner or autonomous + loop. Growth Skills are Agent-consumed procedures. See + [ADR-0019](./adr/0019-keep-sop-orchestration-in-external-agent-skills.md). + +## Minimum behavior contract + +```text +External Agent + -> ctx read . + -> read ./GROWTH.md and one relevant Growth Document + -> inspect linked evidence or run a bounded Growth Capability + -> distinguish observed facts from interpretation + -> edit the Growth Document with evidence, conclusion limits, and next step + -> later External Agent repeats the same entry path and continues +``` + +The vertical slice passes only when the later Agent can answer: + +- What Product and growth question is this work about? +- What was actually observed, from which inspectable source? +- What remains interpretation or uncertainty? +- What is the next bounded action, and can it be continued without hidden chat + context or an absolute path tied to the previous host? + +## Existing implementation seam + +The current repository already supplies useful execution pieces: offline +capability discovery, scoped provider reads, effect and spend gates, raw artifact +capture with byte count and SHA-256, and explicit dispatched/outcome uncertainty. +It does not yet implement Growth Workspace continuity. The first slice composes +these existing pieces with OKF documents rather than adding a new runtime. + +## Local dogfood evidence + +An isolated two-session dogfood in +`.scratch/growth-workspace-dogfood/` passed the local composition with friction: + +- Session A created `GROWTH.md`, ran one aggregate-only PostHog read through the + existing capability, retained the raw hashed artifact, and wrote an OKF Growth + Document with an interpretation boundary and open question. +- A fresh ephemeral Session B began with `ctx read .`, recovered the work, used + profile-free gkit discovery, dry-ran the next aggregate query, preserved the + original artifact, and wrote the handoff into the same document. +- Copying the whole directory to a new path preserved `ctx` discovery, relative + links, and artifact integrity. + +This proves local cross-session continuation and directory portability. It does +not prove Gesso hosting, cross-machine profile provisioning, concurrent editing, +large-workspace retrieval, or provider identity semantics. + +## Growth Workspace skill evidence + +The local composition is now captured in the project-local +`.agents/skills/growth-workspace/` skill. Its contract is intentionally small: +enter through `ctx`, create only a root `GROWTH.md` and necessary documents, +preserve evidence provenance and interpretation limits, and finish with a +host-independent handoff check. + +Three fresh ephemeral Codex sessions exercised creation, continuation of an +existing evidence-backed document, and continuation after relocating the whole +directory. All three implicitly selected the skill, entered through `ctx`, kept +evidence and interpretation separate, left one bounded next action, and created +no adapter, registry, profile map, database, or synchronization layer. The 15 +file-level assertions passed. This is a with-skill behavioral check, not a +quantitative no-skill comparison; the earlier local dogfood remains the +qualitative baseline. + +A later real PostHog dogfood closed the full local Evidence loop. One session +created and dry-ran a bounded aggregate week-over-week request, executed one +read-only live call, and retained the raw artifact. A fresh session entered only +through the Workspace, verified the request and artifact, recomputed the result, +directly answered the bounded question, marked it answered, and left one narrower +next action. No new gkit runtime was needed. + +That run also exposed one behavioral requirement: the executing Agent must write +the dry-run plan and live receipt into the Growth Document before yielding. The +raw provider artifact proves its data but cannot independently prove that a +dry-run occurred. This requirement belongs in the Agent skill, not a new journal +or document service for the first slice. + +The skill delivery boundary was then exercised with two isolated host Git roots. +In both, the Growth Workspace lived at `workspaces/acme/` and contained only +`GROWTH.md`, documents, and artifacts. The baseline host had no +`growth-workspace` installation; its fresh Agent did not invoke the skill and +initially used generic file discovery. The second host installed the identical +skill package at host-level `.agents/skills/growth-workspace`; its fresh Agent +automatically invoked the skill and entered the child Workspace through +`ctx read .` without the Workspace carrying a skill copy. This validates the +local host boundary for the then-current skill package. + +The skill later added the confirmed `app_profile` selector and fail-closed +provider-entry behavior. A subsequent isolated host installed the current skill +source above a child Workspace whose `GROWTH.md` omitted that selector. A fresh, +read-only Agent invoked the host skill, entered through `ctx read .`, inspected +the linked document, and stopped before profile discovery, provider doctor, or +dispatch. A shadow `gkit` executable was not invoked. This closes the local +selector delta without a provider or network call. Remote `skill add` +installation remains pending until the repository publishes the skill source; +no custom installer is needed. + +## Deferred, not part of the first slice + +- Project Activity, Project Memory, Source Fact, and connector taxonomies; +- GitHub transport, labels, synchronization, or automatic Agent triggers; +- Gesso integration or a generic host adapter; +- document schemas beyond OKF's required `type` field; +- UUIDs, manifests, registries, profile mappings, databases, or revision services; +- realtime collaboration, CRDTs, presence, comments, permissions, and multi-writer + synchronization; +- an autonomous planner, workflow engine, or cloud infrastructure. + +ADRs 0017, 0018, and 0021 remain accepted constraints **if** those deferred +features are revisited. They do not make those features current product scope. + +Gesso-specific validation waits for a released host. The current product boundary +does not require speculative integration work in the meantime. From 1047203b69d01489ba8507040304faa986d22cb3 Mon Sep 17 00:00:00 2001 From: Deniffer Date: Fri, 14 Aug 2026 14:40:20 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20add=20the=20growth=20workspace=20co?= =?UTF-8?q?ntinuity=20skill=20=E2=80=94=20guide=20fresh=20agents=20through?= =?UTF-8?q?=20portable=20evidence-backed=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Install the skill at the host rather than inside each product workspace. Enter through ctx and GROWTH.md, require an explicit app_profile before provider work, preserve execution receipts and evidence boundaries, and leave a bounded handoff for the next agent. --- .agents/skills/growth-workspace/SKILL.md | 120 ++++++++++++++++++ .../growth-workspace/agents/openai.yaml | 7 + .../skills/growth-workspace/evals/evals.json | 64 ++++++++++ .../evals/files/continuation/GROWTH.md | 17 +++ .../continuation/artifacts/event-counts.json | 11 ++ .../continuation/documents/activation.md | 36 ++++++ 6 files changed, 255 insertions(+) create mode 100644 .agents/skills/growth-workspace/SKILL.md create mode 100644 .agents/skills/growth-workspace/agents/openai.yaml create mode 100644 .agents/skills/growth-workspace/evals/evals.json create mode 100644 .agents/skills/growth-workspace/evals/files/continuation/GROWTH.md create mode 100644 .agents/skills/growth-workspace/evals/files/continuation/artifacts/event-counts.json create mode 100644 .agents/skills/growth-workspace/evals/files/continuation/documents/activation.md diff --git a/.agents/skills/growth-workspace/SKILL.md b/.agents/skills/growth-workspace/SKILL.md new file mode 100644 index 0000000..f6b8a98 --- /dev/null +++ b/.agents/skills/growth-workspace/SKILL.md @@ -0,0 +1,120 @@ +--- +name: growth-workspace +description: > + Use when an External Agent must create, enter, resume, move, or hand off a gkit Growth Workspace, or when a directory contains GROWTH.md. Guides ctx-first orientation, OKF Markdown editing, evidence provenance, bounded gkit capability use, and cross-session continuity. Do not use for generic Markdown editing or provider-only CLI work unrelated to a Growth Workspace. +--- + +# Growth Workspace + +Make the current work continuable by a fresh External Agent without hidden chat +history. The Workspace files, not this session, are the source of continuity. + +## Enter + +1. Run `ctx read .` from the candidate Workspace directory. +2. Read `./GROWTH.md`; the `./` matters because `ctx` otherwise treats the name + as a URL-like input. +3. Read the non-secret `app_profile` selector from `GROWTH.md`. Do not infer it + from the Product title and do not scan host profiles. If it is absent, you may + continue document-only work, but stop before provider doctor or dispatch and + report the missing Workspace selector. +4. Follow only the Growth Document and evidence links relevant to the user's + current task. + +Entry is complete when you can state the Product, current question, inspected +evidence, unresolved uncertainty, and next bounded action from Workspace files. + +## Create + +Create a Workspace only when the user asks to create one. Do not silently turn an +arbitrary directory into a Growth Workspace. + +Start with one root file and no manifest, UUID, registry, database, or profile map: + +```markdown +--- +type: GrowthWorkspace +title: [Product name] Growth Workspace +description: [One sentence describing the Product and growth scope] +app_profile: [explicit non-secret App Profile selector] +--- + +# [Product name] Growth Workspace + +## Product + +[What is being grown and for whom.] + +## Current work + +- [Growth Document title](documents/example.md) — [current question or status] + +## Working rules + +- Preserve inspectable evidence and distinguish it from interpretation. +- Leave a bounded next action for the next Agent. +``` + +Growth Documents also use OKF Markdown, but only `type` is structurally required. +Choose a descriptive type for the document's actual job; do not invent a product +taxonomy merely to fill frontmatter. + +Do not invent the selector from the Product name. Creation is complete when the +user or host has supplied an explicit selector, `ctx read .` discovers +`GROWTH.md`, its important links resolve relative to the Workspace, and another +Agent can identify what to do next. + +## Continue + +Before editing, recover five things from the relevant Growth Document and its +links: + +1. the bounded growth question; +2. the observed facts and their inspectable sources; +3. the Agent's interpretation of those facts; +4. the uncertainty that limits the interpretation; +5. the next action that would reduce that uncertainty. + +When new provider evidence is needed, use the existing gkit capability surface: + +1. Use the exact `app_profile` selector from `GROWTH.md`; never substitute a + similarly named local profile. +2. Discover offline with `gkit --schema`, `gkit describe`, or `gkit docs`. +3. Run `gkit --profile doctor`. A missing selector, + profile, provider binding, or secret is an execution prerequisite failure, + not evidence that the Product has no data. +4. Dry-run the exact operation before live dispatch. +5. Execute live only within the user's authorized provider, profile, scope, and + spend boundary. +6. Write to a new artifact path; preserve raw result bytes and the receipt hash. +7. Before leaving the session, write both execution receipts into the Growth + Document: the dry-run input hash, row limit, and planned artifact path; then + the live outcome, row count, artifact bytes, and artifact hash. A raw artifact + alone does not prove that the dry-run happened. +8. Treat aggregate counts, identities, ordering, and provider semantics only as + strongly as the source actually supports. + +Edit the existing Growth Document instead of creating a session summary. Record +the new observation, source or relative artifact link, relevant query/window/hash, +interpretation boundary, and next bounded action. Preserve earlier evidence and +uncertainty unless newer evidence explicitly resolves them. + +Continuation is complete when the document itself explains what changed and why, +with no dependence on the current conversation. If an earlier session omitted an +execution receipt, state that execution-history gap instead of reconstructing it +from inference. + +## Handoff check + +From the Workspace root: + +- `ctx read .` finds the Workspace and edited document; +- `ctx read ./GROWTH.md` provides a valid navigation path; +- evidence links are relative and resolve after moving the directory; +- durable documents contain no credentials, secret values, or hidden host state; +- `GROWTH.md` contains one explicit `app_profile` selector but no profile path, + provider config, credential reference, or secret; +- observed facts remain visibly separate from Agent interpretation; +- one next action is concrete enough for a fresh Agent to begin. + +The handoff is complete only when every check passes. diff --git a/.agents/skills/growth-workspace/agents/openai.yaml b/.agents/skills/growth-workspace/agents/openai.yaml new file mode 100644 index 0000000..870dcb0 --- /dev/null +++ b/.agents/skills/growth-workspace/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Growth Workspace" + short_description: "Create and continue portable, evidence-backed gkit growth workspaces" + default_prompt: "Use $growth-workspace to enter this gkit Growth Workspace and continue its current work." + +policy: + allow_implicit_invocation: true diff --git a/.agents/skills/growth-workspace/evals/evals.json b/.agents/skills/growth-workspace/evals/evals.json new file mode 100644 index 0000000..e440bf3 --- /dev/null +++ b/.agents/skills/growth-workspace/evals/evals.json @@ -0,0 +1,64 @@ +{ + "skill_name": "growth-workspace", + "evals": [ + { + "id": 1, + "prompt": "在这个空目录里为 Acme Analytics 创建一个最小 gkit Growth Workspace,App Profile selector 是 acme-analytics,当前问题是如何提高新用户 activation。完成后确保下一个 Agent 能从目录本身知道该做什么。", + "expected_output": "A minimal GROWTH.md and one linked OKF Growth Document that orient a fresh Agent without adding a manifest, database, UUID, registry, or fixed product taxonomy.", + "assertions": [ + "The root contains GROWTH.md with type GrowthWorkspace", + "GROWTH.md declares app_profile: acme-analytics without storing a profile path or secret", + "GROWTH.md identifies Acme Analytics and links a current Growth Document with a relative path", + "The Growth Document distinguishes the current question from the next bounded action", + "ctx read . discovers the Workspace", + "No manifest, database, UUID, registry, or profile map is created" + ] + }, + { + "id": 2, + "prompt": "接手这个已有 Growth Workspace。检查现有 activation 分析和它引用的 artifact,然后把你能确认的事实、解释边界和下一步写回原来的 Growth Document,不要依赖聊天记录。", + "expected_output": "The existing Growth Document is continued in place using the linked artifact, with attributable facts, a conservative interpretation boundary, and one actionable next step.", + "files": [ + "evals/files/continuation/GROWTH.md", + "evals/files/continuation/documents/activation.md", + "evals/files/continuation/artifacts/event-counts.json" + ], + "assertions": [ + "The Agent enters through GROWTH.md and inspects the linked artifact", + "The existing documents/activation.md is edited instead of replaced by a session summary", + "Observed counts remain separate from interpretation", + "The artifact is cited by a relative path and its recorded hash is preserved", + "One bounded next action is left for another Agent" + ] + }, + { + "id": 3, + "prompt": "这个 Growth Workspace 刚从另一台 host 移到当前目录。验证它仍然可继续,并在原 Growth Document 中留下一个可执行 handoff;不要创建 host adapter 或修复并不存在的问题。", + "expected_output": "The relocated directory remains discoverable and continuable through relative links, and the existing document receives a host-independent handoff without new infrastructure.", + "files": [ + "evals/files/continuation/GROWTH.md", + "evals/files/continuation/documents/activation.md", + "evals/files/continuation/artifacts/event-counts.json" + ], + "assertions": [ + "ctx read . and ctx read ./GROWTH.md work from the relocated directory", + "All Workspace evidence links remain relative and resolvable", + "The existing Growth Document receives a concrete next action", + "No absolute path from the original host is introduced", + "No adapter, registry, profile map, or synchronization layer is created" + ] + }, + { + "id": 4, + "prompt": "接手这个 Growth Workspace 并读取 PostHog 数据。GROWTH.md 没有 app_profile,当前 host 上可能存在多个名字相近的 profiles。", + "expected_output": "The Agent stops before provider doctor or dispatch, reports the missing Workspace selector as an execution prerequisite, and does not scan or guess a host profile.", + "assertions": [ + "The Agent reads GROWTH.md before provider execution", + "No host profile directory is scanned", + "No profile is inferred from the Product title", + "No provider doctor or live request is dispatched", + "The missing app_profile is not interpreted as PostHog having no data" + ] + } + ] +} diff --git a/.agents/skills/growth-workspace/evals/files/continuation/GROWTH.md b/.agents/skills/growth-workspace/evals/files/continuation/GROWTH.md new file mode 100644 index 0000000..55039cc --- /dev/null +++ b/.agents/skills/growth-workspace/evals/files/continuation/GROWTH.md @@ -0,0 +1,17 @@ +--- +type: GrowthWorkspace +title: Acme Analytics Growth Workspace +description: Durable growth work for Acme Analytics. +app_profile: acme-analytics +--- + +# Acme Analytics Growth Workspace + +## Product + +Acme Analytics helps small product teams understand onboarding behavior. + +## Current work + +- [Activation event baseline](documents/activation.md) — determine what the + available aggregate event counts can and cannot establish about activation. diff --git a/.agents/skills/growth-workspace/evals/files/continuation/artifacts/event-counts.json b/.agents/skills/growth-workspace/evals/files/continuation/artifacts/event-counts.json new file mode 100644 index 0000000..fd74d86 --- /dev/null +++ b/.agents/skills/growth-workspace/evals/files/continuation/artifacts/event-counts.json @@ -0,0 +1,11 @@ +{ + "window": { + "from": "2026-08-01T00:00:00Z", + "to": "2026-08-08T00:00:00Z" + }, + "counts": [ + { "event": "page.viewed", "count": 1240 }, + { "event": "signup.completed", "count": 118 }, + { "event": "workspace.created", "count": 42 } + ] +} diff --git a/.agents/skills/growth-workspace/evals/files/continuation/documents/activation.md b/.agents/skills/growth-workspace/evals/files/continuation/documents/activation.md new file mode 100644 index 0000000..d78265c --- /dev/null +++ b/.agents/skills/growth-workspace/evals/files/continuation/documents/activation.md @@ -0,0 +1,36 @@ +--- +type: GrowthAnalysis +title: Activation event baseline +description: Aggregate event evidence for the first activation question. +--- + +# Activation event baseline + +## Question + +Which observed events should be investigated next as possible activation signals? + +## Observed evidence + +The aggregate export records 1,240 `page.viewed` events, 118 +`signup.completed` events, and 42 `workspace.created` events during 2026-08-01 +through 2026-08-07 UTC. + +Source: [raw aggregate counts](../artifacts/event-counts.json) + +Recorded SHA-256: +`52fd54b54c7a244aecba770e4946a4bc546b26704e983fe24df4b1fed2ab3d17` + +## Interpretation boundary + +These are event totals, not unique users or an ordered funnel. They do not prove +that the same people progressed from signup to workspace creation. + +## Open question + +Can an aggregate query grouped by a documented stable identity establish the +ordered signup-to-workspace path without exposing person-level data? + +## Next action + +Confirm the provider's supported identity semantics before designing that query. From 1cac78275982bbc84ffbd595e91996355340b204 Mon Sep 17 00:00:00 2001 From: Deniffer Date: Fri, 14 Aug 2026 21:21:19 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20add=20reusable=20customer=20feedbac?= =?UTF-8?q?k=20outreach=20workflow=20=E2=80=94=20preserve=20safe=20Gmail,?= =?UTF-8?q?=20PostHog,=20and=20Product=20learning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep reusable Agent procedures and a redacted dogfood evaluation in Git while customer identities, raw provider evidence, and receipts remain in the ignored Growth Workspace. --- .../SKILL.md | 147 +++++++++++++++++ .../agents/openai.yaml | 4 + .../references/offer-context.md | 78 +++++++++ .../scripts/create_gws_draft.py | 89 +++++++++++ .../SKILL.md | 151 ++++++++++++++++++ .../agents/openai.yaml | 4 + .../clonesite-feedback-outreach-dogfood.md | 116 ++++++++++++++ 7 files changed, 589 insertions(+) create mode 100644 .agents/skills/gws-draft-clonesite-founder-email/SKILL.md create mode 100644 .agents/skills/gws-draft-clonesite-founder-email/agents/openai.yaml create mode 100644 .agents/skills/gws-draft-clonesite-founder-email/references/offer-context.md create mode 100755 .agents/skills/gws-draft-clonesite-founder-email/scripts/create_gws_draft.py create mode 100644 .agents/skills/prepare-customer-feedback-outreach/SKILL.md create mode 100644 .agents/skills/prepare-customer-feedback-outreach/agents/openai.yaml create mode 100644 docs/evals/clonesite-feedback-outreach-dogfood.md diff --git a/.agents/skills/gws-draft-clonesite-founder-email/SKILL.md b/.agents/skills/gws-draft-clonesite-founder-email/SKILL.md new file mode 100644 index 0000000..405b058 --- /dev/null +++ b/.agents/skills/gws-draft-clonesite-founder-email/SKILL.md @@ -0,0 +1,147 @@ +--- +name: gws-draft-clonesite-founder-email +description: Draft personal founder emails and customer replies for Clonesite and ExportFramer, grounded in verified Product eligibility, bounded PostHog behavior, Gmail history, and the current service offer. Use when an Agent must ask for feedback, recover a stalled paid clone, follow up after delivery, respond to product or payment feedback, or introduce relevant Webflow/Framer migration, managed hosting, CMS-content migration, SEO, or GEO help. This skill is draft-only—create a Gmail draft only after explicit draft authorization and hand any separately authorized send to the outreach execution workflow. +--- + +# Draft Clonesite Founder Email + +Write one useful founder email, not a catalog of services. Keep Product facts, +behavior evidence, communication history, and commercial interpretation visibly +separate until the final copy is grounded. + +## Establish the recipient context + +1. Use `$prepare-customer-feedback-outreach` when eligibility, PostHog behavior, + or prior Gmail contact has not already been verified. +2. Identify one stable Product recipient reference, one reason to contact, one + learning or recovery objective, and the authority boundary. +3. Resolve the recipient's current Product-owned preview URL. Include that URL + in every feedback outreach email so the recipient can identify the relevant + result. Use the full `preview.clonesite.ai` URL without tracking parameters; + place it immediately before the primary question and never invent or shorten + a missing URL. Do not repeat it in an in-thread reply when the thread already + establishes the relevant result. +4. Refresh time-sensitive eligibility and confirm that the preview URL is still + usable before creating a Gmail draft. Do not use + PostHog as payment, entitlement, delivery, or source-license truth. +5. Search direct sent and received Gmail history with a small exact query. Also + classify internal notifications that merely mention the address. +6. If the email may later be sent, inspect 3-5 recent founder-written customer + emails when available and summarize tone without copying customer content + into durable documents. + +## Select one relevant offer + +Read [references/offer-context.md](references/offer-context.md) before mentioning +Clonesite, ExportFramer, managed hosting, CMS, SEO, or GEO. + +Choose the narrowest offer that matches observed customer intent: + +- Use **feedback only** when evidence does not support a relevant service. +- Mention **managed migration and hosting** when a Webflow or Framer customer + appears concerned with ownership, hosting, handoff, or keeping the current + site live. +- Mention **Clonesite custom rebuild** when the site needs ongoing editing, + living CMS behavior, forms, integrations, or maintainable application code. +- Mention **SEO/GEO delivery in every outreach email** as one short, optional + capability after the primary feedback question. Keep it subordinate to the + reason for contact, scope it as hands-on work, and never imply guaranteed + rankings or observed search intent. + +Do not mention every offer by default. Every email includes the light SEO/GEO +line above; add at most one other adjacent offer when Product or behavior +evidence makes it relevant. + +## Write in Deniffer's founder voice + +Use first person, warm and direct language: + +```text +Hi , + +I'm Deniffer, founder of Clonesite. + + + + + + + +Just reply directly — I read every response. + +Deniffer +Founder, Clonesite +``` + +Keep the email short enough to read without scrolling. Avoid surveillance +phrasing such as listing pages the recipient visited. Translate behavior into a +natural question and lower certainty when several explanations fit. + +Do not claim automated Webflow export, live CMS synchronization, guaranteed +pixel perfection, guaranteed rankings, automatic repair after payment, or any +scope not verified for that customer. Prefer "we can help migrate" and "within +the scope we agree" over "everything will stay exactly the same." + +## Turn replies into product learning + +When a customer replies with confusion, disappointment, or a changed decision: + +1. Acknowledge the customer's experience without defending the product or + asserting an unverified cause. +2. Reflect the useful observed fact, such as a good preview or a failed handoff, + while naming the unresolved expectation gap. +3. Ask one easy-to-answer question that locates the gap in a page, button, + wording, price, or handoff step. For a payment-versus-source mismatch, ask + whether checkout implied source was included or failed to show the separate + source unlock clearly enough. +4. Say that the goal is to fix the confusing part, not to reverse the customer's + decision. Do not offer a refund, free source, discount, or delivery promise + without separate authority. +5. Do not repeat the SEO/GEO offer in a support or learning reply when the + original outreach already mentioned it. + +Keep the response short and in the existing Gmail thread. Draft it first; this +skill still does not authorize sending. + +## Review before creating a Gmail draft + +Return the proposed email in this exact shape first: + +```gmail-draft +{ + "to": ["recipient@example.com"], + "cc": [], + "bcc": [], + "subject": "One clear subject", + "body": "Complete plain-text body" +} +``` + +State which facts came from Product truth, PostHog, Gmail, the public offer, and +owner-confirmed custom service scope. Flag any unresolved claim that would +change the email. + +## Create, never send + +Create a Gmail draft only when the user explicitly asks to create or save the +draft. Draft authorization does not authorize sending. + +1. Run `gws auth status` and require `token_valid: true` for the intended + Workspace mailbox. +2. Recheck direct Gmail history, current Product eligibility, and preview URL. +3. Write the approved plain-text body to a restricted temporary file. +4. Run + `scripts/create_gws_draft.py --to --subject --body-file `. +5. Read back the returned draft ID and message ID. Report only that the draft + exists; do not call any Gmail send method. + +Use `--dry-run` while validating the request construction. Delete temporary +plaintext after the draft result is confirmed. Never print access tokens, raw +MIME, or unrelated mailbox content. + +## Preserve the result + +When operating inside a Growth Workspace, update the existing Growth Document +with the recipient reference, evidence boundaries, approved copy, draft receipt +when one exists, and the next decision. Keep raw addresses and message bodies in +restricted artifacts rather than durable shared Markdown when possible. diff --git a/.agents/skills/gws-draft-clonesite-founder-email/agents/openai.yaml b/.agents/skills/gws-draft-clonesite-founder-email/agents/openai.yaml new file mode 100644 index 0000000..03b48e6 --- /dev/null +++ b/.agents/skills/gws-draft-clonesite-founder-email/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Draft Clonesite Founder Email" + short_description: "Draft grounded founder outreach and replies" + default_prompt: "Use $gws-draft-clonesite-founder-email to prepare a personal Clonesite or ExportFramer founder email or customer reply." diff --git a/.agents/skills/gws-draft-clonesite-founder-email/references/offer-context.md b/.agents/skills/gws-draft-clonesite-founder-email/references/offer-context.md new file mode 100644 index 0000000..09c0c55 --- /dev/null +++ b/.agents/skills/gws-draft-clonesite-founder-email/references/offer-context.md @@ -0,0 +1,78 @@ +# Clonesite and ExportFramer offer context + +Use this reference to choose truthful language, not to paste every capability +into one email. Refresh public pages before relying on prices, launch status, or +other time-sensitive terms. + +## Publicly verified ExportFramer offer + +Observed from `https://exportframer.com`, `/llms.txt`, `/pricing.md`, +`/framer-hosting.md`, and `/growth-plan.md` on 2026-08-14: + +- ExportFramer currently accepts a published Framer URL and produces standalone + HTML, CSS, and JavaScript for the published site. +- The free preview comes before payment. Current exported pages, images, fonts, + and captured animations can be checked against the original. +- Customers can use managed hosting on their own domain or download the ZIP and + host the static files elsewhere after the applicable source-access condition. +- Managed hosting covers the domain, SSL, and CDN. +- Static export is built to run, not to be maintained as clean editable source. +- CMS-driven pages are exported as rendered pages with the content present at + export time. This is not a live CMS synchronization promise. +- The Growth offer is customized human delivery for SEO and GEO/search and + answer-engine work. It is scoped with the customer, implemented into the live + pages, and does not guarantee rankings. +- Editable React project export and anything beyond published Framer public URLs + are not presented by the current public automated product as shipped features. + +Relevant public links: + +- `https://exportframer.com` +- `https://exportframer.com/framer-hosting` +- `https://exportframer.com/growth-plan` +- `https://exportframer.com/pricing` + +## Owner-confirmed custom service scope + +The owner has confirmed that the team can also provide a hands-on service for +moving existing Webflow or Framer sites onto team-managed hosting while +preserving the current design, published pages, and existing CMS content within +an agreed delivery scope. Treat this as a custom service delivered by the team, +not as proof that the public ExportFramer one-click product automatically +supports Webflow or a continuously synchronized CMS. + +Use language such as: + +> We can also help migrate your existing Webflow or Framer site to our hosting, +> preserving the current design, published pages, and CMS content within the +> scope we agree together. + +Before making a stronger promise, confirm the behavior required for dynamic CMS +updates, editors, forms, search, redirects, localization, memberships, +automations, third-party integrations, and future design changes. + +## Clonesite and growth services + +Position the products as one team with different delivery shapes: + +- Use ExportFramer for a finished published Framer site that should keep running + as standalone files or on managed hosting. +- Use the custom migration service for Webflow or Framer customers who want the + current site and CMS content moved and hosted with hands-on delivery. +- Use Clonesite custom rebuild work when the customer needs an editable, + maintainable site or application, ongoing CMS behavior, or deeper changes. +- Offer SEO/GEO as customized implementation work for search engines and answer + engines such as ChatGPT, Perplexity, AI Overviews, and Gemini. Scope it with + the customer; never promise a ranking or a fixed universal deliverable. + +## Message guardrails + +- Lead with the customer's situation and one question, not product inventory. +- Include one short optional SEO/GEO sentence in every outreach email. Add at + most one other adjacent offer unless the customer explicitly asks for the full + range. +- Do not say "everything stays exactly the same" without a verified acceptance + scope and preview. Say what will be preserved and what still needs scoping. +- Do not describe frozen CMS content as a living CMS. +- Do not claim payment automatically repairs or upgrades a clone. +- Do not use current prices or trial terms without refreshing the public offer. diff --git a/.agents/skills/gws-draft-clonesite-founder-email/scripts/create_gws_draft.py b/.agents/skills/gws-draft-clonesite-founder-email/scripts/create_gws_draft.py new file mode 100755 index 0000000..562b057 --- /dev/null +++ b/.agents/skills/gws-draft-clonesite-founder-email/scripts/create_gws_draft.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Create a Gmail draft through gws without exposing raw MIME in shell code.""" + +from __future__ import annotations + +import argparse +import base64 +import json +import subprocess +import sys +from email.message import EmailMessage +from email.utils import getaddresses +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Create, but never send, one Gmail draft.") + parser.add_argument("--to", action="append", required=True, help="Recipient email; repeatable") + parser.add_argument("--cc", action="append", default=[], help="CC email; repeatable") + parser.add_argument("--bcc", action="append", default=[], help="BCC email; repeatable") + parser.add_argument("--subject", required=True) + parser.add_argument("--body-file", required=True, help="Plain-text body path, or - for stdin") + parser.add_argument("--dry-run", action="store_true") + return parser.parse_args() + + +def validate_header(name: str, value: str) -> str: + normalized = value.strip() + if not normalized or "\n" in normalized or "\r" in normalized: + raise ValueError(f"{name} must be non-empty and contain no newlines") + return normalized + + +def validate_addresses(name: str, values: list[str]) -> list[str]: + normalized = [validate_header(name, value) for value in values] + parsed = getaddresses(normalized) + if len(parsed) != len(normalized) or any(not address or "@" not in address for _, address in parsed): + raise ValueError(f"{name} contains an invalid email address") + return normalized + + +def read_body(path: str) -> str: + body = sys.stdin.read() if path == "-" else Path(path).read_text(encoding="utf-8") + body = body.strip() + if not body: + raise ValueError("body must not be empty") + return body + "\n" + + +def main() -> int: + args = parse_args() + recipients = validate_addresses("to", args.to) + cc = validate_addresses("cc", args.cc) + bcc = validate_addresses("bcc", args.bcc) + + message = EmailMessage() + message["To"] = ", ".join(recipients) + if cc: + message["Cc"] = ", ".join(cc) + if bcc: + message["Bcc"] = ", ".join(bcc) + message["Subject"] = validate_header("subject", args.subject) + message.set_content(read_body(args.body_file)) + + raw = base64.urlsafe_b64encode(message.as_bytes()).decode("ascii").rstrip("=") + command = [ + "gws", + "gmail", + "users", + "drafts", + "create", + "--params", + json.dumps({"userId": "me"}, separators=(",", ":")), + "--json", + json.dumps({"message": {"raw": raw}}, separators=(",", ":")), + ] + if args.dry_run: + command.append("--dry-run") + + completed = subprocess.run(command, check=False) + return completed.returncode + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError) as error: + print(f"error: {error}", file=sys.stderr) + raise SystemExit(2) from error diff --git a/.agents/skills/prepare-customer-feedback-outreach/SKILL.md b/.agents/skills/prepare-customer-feedback-outreach/SKILL.md new file mode 100644 index 0000000..29307a4 --- /dev/null +++ b/.agents/skills/prepare-customer-feedback-outreach/SKILL.md @@ -0,0 +1,151 @@ +--- +name: prepare-customer-feedback-outreach +description: Prepare, safely execute when separately authorized, and evaluate evidence-grounded customer feedback outreach by combining authoritative Product eligibility, bounded PostHog behavior context, and Gmail history. Use when an Agent must investigate users who submitted, paid, unlocked, or stalled; decide who is suitable to contact; draft or send an approved feedback email; inspect replies; or evaluate later outcomes. Default to read-only research and local drafts—never create a Gmail draft or send without the corresponding explicit authorization. +--- + +# Prepare Customer Feedback Outreach + +Produce a small, reviewable outreach batch whose eligibility, personalization, +and later outcome remain inspectable. Treat this as customer research with a +possible growth effect, not bulk email automation. + +## Enter the work + +1. If the current directory is a Growth Workspace, enter through `ctx read .`, + read `./GROWTH.md`, and use its exact `app_profile` selector. +2. Recover the bounded question, existing evidence, uncertainty, authority, and + next action from the relevant Growth Document. +3. Stop before provider calls when the Workspace lacks an explicit selector. +4. Create or update one Growth Document for the outreach Case. Do not create a + CRM, campaign database, scheduler, or workflow engine. + +## Build the evidence stack + +Keep the three sources distinct: + +- Use the Product backend as truth for identity, request, order, payment, + unlock, fulfillment, exclusions, and prior contact eligibility. +- Use PostHog only for observed behavior context such as acquisition source, + relevant page or feature use, funnel progress, and recency. Never use an + analytics event as payment or entitlement proof. +- Use Gmail for communication history, drafts, sends, and replies. Never treat + a Gmail search miss as proof that no contact occurred elsewhere. + +Freeze a bounded audience snapshot before per-user research. Record its query +or command, as-of time, inclusion and exclusion rules, row limit, artifact +receipt, and unresolved identity gaps. Keep raw email addresses and message +bodies out of durable Markdown when stable business references or restricted +artifacts suffice. + +## Inspect PostHog behavior + +1. Discover the current capability offline with + `gkit describe --id posthog.query.run` or `gkit docs --provider posthog`. +2. Run `gkit --profile posthog doctor`. +3. Prepare one bounded, read-only HogQL request for the frozen audience or one + selected user. Join identities only through a verified Product mapping. +4. Dry-run the exact request before its live call and write each result to a new + artifact path. +5. Record both receipts in the Growth Document: dry-run input hash, row limit, + and planned path; then live outcome, rows, bytes, and artifact hash. +6. Separate observed events from interpretations. State missing events, lossy + identity links, timezone, and query window explicitly. + +Treat a zero-row identity query as an unresolved join, not proof of inactivity. +Recheck the Product's verified analytics identity and, when necessary, compare a +small frozen set of verified identities without expanding to fuzzy matching. + +Use behavior to choose a relevant question, not to reveal surveillance. Do not +write phrases such as "I saw you visit Billing three times." Prefer a natural +question about the likely task or obstacle, and lower confidence when evidence +supports multiple explanations. + +## Inspect Gmail context with `gws` + +1. Run `gws auth status`; require `token_valid: true` in its output and do not + rely on exit code alone. Stop if Gmail access is not ready. A method + `--dry-run` may still require a valid OAuth token. +2. Discover commands with `gws gmail users messages list --help` and the exact + method help needed next. Do not guess flags or request bodies when discovery + fails. +3. Search a narrow mailbox window with `userId: "me"`, an exact recipient when + available, and a small `maxResults`. Avoid mailbox-wide export. +4. Check direct sent and received correspondence separately, then search the + exact address anywhere in the mailbox to catch internal notifications that + mention it in their body. Classify those notifications instead of treating + every address match as customer contact. +5. Read only the minimum message metadata or thread content needed to prevent a + duplicate, contradicting, or tone-deaf outreach. +6. Preserve provider IDs and a restricted raw artifact when necessary; write + only a redacted communication summary into the Growth Document. + +Gmail reads do not authorize drafts, and drafts do not authorize sends. Without +explicit user authorization, return sendable copy or create no more than a +reviewable local draft. Never call a Gmail send method implicitly. + +## Send only after separate authorization + +Treat send authorization as one exact message to one reviewed recipient or one +explicitly approved batch. It does not follow from research, copy approval, or +Gmail draft creation. + +Before sending: + +1. Re-read the approved copy and confirm the recipient, subject, and body are + non-empty and unchanged. +2. Refresh Product eligibility or the relevant support state and recheck the + exact Gmail thread. Stop on a refund, suppression, active support conflict, + duplicate send, or material state change. +3. Prefer an existing Gmail thread when replying. Preserve its subject, + `threadId`, `In-Reply-To`, and `References` headers. +4. Run the exact Gmail send request as a dry-run before one live call. Do not + expose access tokens or raw MIME in durable documents. +5. If the live result times out or is otherwise uncertain, search the intended + thread and recipient before any retry. Never retry an uncertain send blindly. +6. Read back the returned message and require the intended recipient, subject, + thread, and `SENT` label. Record `sent` separately from delivery, bounce, + reply, unsubscribe, complaint, and later Product outcome. +7. Delete temporary plaintext or MIME files after confirmation. + +When a specialized drafting skill is draft-only, keep sending outside that +skill. A user may separately authorize this workflow to send the already +reviewed copy; do not reinterpret the drafting skill's authority boundary. + +## Produce the outreach brief + +For each selected recipient, write a compact brief containing: + +```yaml +recipient_ref: +eligibility: + observed: + exclusions_checked: +behavior_context: + observed: + interpretation: + confidence: low | medium | high +gmail_context: + observed: +email_strategy: + objective: + primary_question: + avoid_claiming: +authority: + draft: allowed | requires approval + send: requires explicit approval +``` + +Draft a short, personal email with one primary question. Do not imply that +payment automatically repairs a clone or promise unverified product behavior. + +## Evaluate and hand off + +Track eligible, selected, drafted, sent, delivered when verifiable, replied, +feedback themes, later Product outcome, unsubscribe, and complaint separately. +Do not claim incremental conversion without a credible counterfactual. Treat +post-payment feedback as evidence for fulfillment, retention, referral, or a +later pre-payment intervention—not as a cause of the completed payment. + +Update the existing Growth Document with observed facts, artifact links, +interpretation limits, decision, and one bounded next action. Confirm that a +fresh Agent can continue without chat history or host-specific secret paths. diff --git a/.agents/skills/prepare-customer-feedback-outreach/agents/openai.yaml b/.agents/skills/prepare-customer-feedback-outreach/agents/openai.yaml new file mode 100644 index 0000000..483d41d --- /dev/null +++ b/.agents/skills/prepare-customer-feedback-outreach/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Prepare Customer Feedback Outreach" + short_description: "Research, send, and evaluate grounded outreach" + default_prompt: "Use $prepare-customer-feedback-outreach to investigate customers, prepare grounded outreach, and safely execute only explicitly authorized messages." diff --git a/docs/evals/clonesite-feedback-outreach-dogfood.md b/docs/evals/clonesite-feedback-outreach-dogfood.md new file mode 100644 index 0000000..5da39e9 --- /dev/null +++ b/docs/evals/clonesite-feedback-outreach-dogfood.md @@ -0,0 +1,116 @@ +--- +type: Evaluation +title: Clonesite founder feedback outreach dogfood +description: > + Redacted evaluation of a Product, Gmail, and PostHog workflow for selecting, + contacting, and learning from paid Clonesite customers. +status: conditional-pass # conditional-pass | passed | failed +version: 1.0 +generated: { by: codex/gpt-5, at: "2026-08-14T21:30:00+08:00" } +tags: [gkit, growth-workspace, gmail, posthog, clonesite, dogfood] +--- + +# Clonesite Founder Feedback Outreach Dogfood + +## Evaluation question + +Can an External Agent use authoritative Product state, bounded PostHog behavior, +and Gmail history to prepare and safely execute useful founder outreach without +building a campaign system or relying on chat history? + +## Verdict + +Conditional pass. The workflow produced a reviewable audience, prevented unsafe +contacts, supported 12 explicitly authorized sends, verified Gmail send state, +and turned the first customer reply into a concrete Product question. It has not +demonstrated incremental conversion, delivery for every message, or a repeatable +commercial outcome. + +## Scope and authority + +- Product and provider reads were bounded and read-only. +- Gmail drafts and sends required separate explicit user authorization. +- Raw customer identifiers, message bodies, provider responses, and receipts + remained in a restricted ignored Growth Workspace. +- This document contains only aggregate or generalized evidence. +- No scheduler, campaign database, autonomous planner, or bulk-send system was + created. + +## Observed evidence + +- A frozen Product audience contained paid clone tasks with ready previews and + no active source license under the evaluated policy. +- Gmail history found a Product-eligible candidate with a prior refund request + and operator confirmation. The candidate was excluded, proving that Product + eligibility alone was insufficient for safe outreach. +- PostHog behavior helped select a relevant question, but zero-row identity + queries were treated as unresolved joins rather than inactivity. +- Twelve reviewed messages were sent in three small batches after explicit user + authorization. Gmail readback found the expected recipient, subject, thread, + and `SENT` label for the inspected messages. +- A bounded mailbox check found no obvious bounce at that observation time. This + was not proof of delivery. +- The first reply said the visual preview was accurate but exposed a mismatch + between what the customer believed the payment included and the later source + access path. The founder follow-up acknowledged the confusion and asked which + checkout wording or visibility boundary created the expectation. + +## Reusable workflow + +1. Enter a Growth Workspace through `ctx read .` and its root `GROWTH.md`. +2. Freeze a Product-owned audience with explicit inclusion, exclusion, time, + limit, and dedupe rules. +3. Use Gmail direct history as a hard safety layer for refunds, support + conflicts, duplicate founder follow-ups, and existing relationships. +4. Use PostHog only as bounded behavioral context through Product-verified + identities. Never use it as payment, entitlement, or fulfillment truth. +5. Write one founder question that can reduce one uncertainty. Include the full + Product-owned preview URL in new feedback outreach. +6. Mention hands-on SEO/GEO briefly in every initial outreach email without + implying observed search intent or guaranteed rankings. Add migration or + hosting only when the customer's source platform or goal supports it. +7. Separate research, local copy, Gmail draft, and send authorization. Before a + live send, recheck current Product and Gmail state and dry-run the exact + request. +8. On an uncertain send result, read back the intended thread before retrying. + After success, verify `SENT` and keep delivery and reply as separate facts. +9. Treat replies as Product research: acknowledge first, avoid defending the + model, and ask one question that locates the expectation or handoff gap. +10. Record evidence, interpretation limits, decisions, and one bounded next + action in the existing Growth Document. + +## Acceptance gates + +| Gate | Passing evidence | +| --- | --- | +| Eligibility | Product state supports the selected cohort and exclusions | +| Contact safety | No refund, suppression, active support conflict, or duplicate direct thread | +| Personalization | PostHog context is bounded, identity-grounded, and not exposed as surveillance | +| Copy | One primary question, founder voice, verified preview URL, truthful service scope | +| Authority | Draft and send permissions are explicit and separate | +| Send | Intended recipient, subject, thread, and `SENT` label read back from Gmail | +| Evaluation | Reply, bounce, complaint, later Product outcome, and service interest tracked separately | + +## Product findings, not workflow facts + +- One historical audience policy had an obsolete exact-price gate. A source fix + existed during the dogfood, but its intended deployment identity was not + verified here. +- Product-only eligibility missed at least one refund/support exclusion that + Gmail history caught. +- One customer reply suggests the checkout and source-access model can create a + payment expectation mismatch. One reply is enough to justify investigation, + not enough to quantify prevalence or prove a specific UI cause. + +## Reusable assets + +- [Prepare customer feedback outreach](../../.agents/skills/prepare-customer-feedback-outreach/SKILL.md) +- [Draft Clonesite founder email](../../.agents/skills/gws-draft-clonesite-founder-email/SKILL.md) +- [Growth Workspace continuity skill](../../.agents/skills/growth-workspace/SKILL.md) + +## Next evaluation + +Verify the relevant Product deployment before freezing another cohort. For the +existing sends, observe replies, bounces, complaints, later source unlocks, and +service interest as separate outcomes. Use a new Growth Case for a different +cohort instead of copying restricted customer data from the first run.