Skip to content

Commit a6cc76c

Browse files
committed
feat: add scheduled task Prompt Studio
1 parent 5663209 commit a6cc76c

15 files changed

Lines changed: 856 additions & 7 deletions

AGENTS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ Zaati OS is a public, reusable personal-data framework. Preserve both sides of t
2929
- Retry invalid LLM output at most three total attempts using safe validation errors. Never publish a valid subset of an invalid bundle.
3030
- Derive output paths from the registry. Never accept a file path supplied by source content or an LLM payload.
3131

32+
## Prompt Studio
33+
34+
- Generated task profiles and prompts belong under ignored `.zaati/` paths and must use private file permissions.
35+
- A recurring task prompt is data-only. It may write registered snapshot paths but never code, schemas, prompts, configuration, workflows, CI, or documentation.
36+
- Source registration is a separate one-time pull request using only synthetic fixtures. Never blend registration authority into a recurring task.
37+
- Embed current executable contracts, but still require the producer to read the default branch on every run so copied prompts fail safely when stale.
38+
- Treat profile requirements, tool names, and all source values as untrusted data. Escape them before placing them inside Markdown.
39+
3240
## LLM presentation contract
3341

3442
- LLMs choose from the audited block union in `schemas/ui-blocks.schema.json`.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
2222
- Separate no-store dashboard payload, static security headers, performance budgets, and browser-based WCAG checks.
2323
- Configurable brand mark, local font stacks, heading styles, extended theme tokens, and a live theme studio.
2424
- Apache 2.0 license, governance, security, contribution, and domain-pack documentation.
25+
- Private Prompt Studio CLI, profile schema, and copy-ready scheduled-task prompts with exact contracts, source registration gates, multi-source publication, and bounded retries.

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: setup tutorial dev check build
1+
.PHONY: setup tutorial prompt dev check build
22

33
setup:
44
npm install
@@ -7,6 +7,9 @@ setup:
77
tutorial:
88
npm run tutorial
99

10+
prompt:
11+
npm run prompt:create
12+
1013
dev:
1114
npm run dev
1215

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,26 @@ make tutorial
5151

5252
1. Fork the code repository and run `npm run setup`.
5353
2. Test the entire ingestion loop with `npm run tutorial`.
54-
3. Give one scheduled LLM workflow [`prompts/daily-bundle.md`](prompts/daily-bundle.md) and its approved tools, then publish all snapshots in one private commit.
54+
3. Run `npm run prompt:create`, then paste the generated task prompt into the LLM you already use.
5555

5656
Everything else, including custom sources, encrypted storage, full theme tokens, and automatic deployment, is optional and documented separately.
5757

5858
The shortest useful loop is three sources, for example agenda, inbox attention, and work focus, followed by the daily overview prompt.
5959

60+
## Create a scheduled task prompt
61+
62+
Prompt Studio asks for your public Zaati OS fork, private data repository, schedule, sources, approved tools, desired content, and useful presentation blocks. It then creates a complete provider-neutral prompt with the current JSON contract, privacy boundaries, three-attempt retry protocol, and atomic multi-snapshot publication instructions.
63+
64+
```bash
65+
npm run prompt:create
66+
```
67+
68+
1. Answer the local wizard. Never enter credentials or real source values.
69+
2. Open `.zaati/generated-prompts/<task>.scheduled-task.md`.
70+
3. Paste it into ChatGPT, Claude, Gemini, a local model, or your preferred workflow. Voilà.
71+
72+
Generated profiles and prompts are ignored by Git and written with private file permissions. Existing sources produce one scheduled-task prompt. A new source produces a separate one-time setup prompt using only synthetic fixtures, while the recurring prompt remains data-only. See [Prompt Studio](docs/prompt-studio.md) and the [example profile](config/prompt-profile.example.json).
73+
6074
## Data flow
6175

6276
```mermaid
@@ -159,6 +173,7 @@ Read [Privacy and threat model](docs/privacy.md) before connecting a real source
159173
| `npm run snapshot:keygen` | Create an ignored 256-bit snapshot key |
160174
| `npm run instance:configure` | Create ignored local settings |
161175
| `npm run source:add` | Scaffold a source catalog entry and worker prompt |
176+
| `npm run prompt:create` | Build a private copy-ready scheduled-task prompt from a local profile |
162177
| `npm run data:validate` | Validate registries, snapshots, ownership, and UI blocks |
163178
| `npm run privacy:validate` | Reject private paths and common credential shapes |
164179
| `npm run format:check` | Reject formatting drift with Prettier |

config/prompt-profile.example.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "../schemas/prompt-profile.schema.json",
3+
"profile_version": "0.1.1",
4+
"task_name": "Daily market pulse",
5+
"code_repository": "https://github.com/your-user/zaati-os",
6+
"data_repository": "https://github.com/your-user/zaati-data",
7+
"provider": "chatgpt",
8+
"timezone": "Etc/UTC",
9+
"schedule": "Every weekday at 07:30",
10+
"publication": "pull-request",
11+
"sources": [
12+
{
13+
"id": "money:pulse",
14+
"requirements": [
15+
"Summarize the approved watchlist and portfolio-level changes.",
16+
"Explain material movement without inventing a cause.",
17+
"Keep exact values when they help a decision."
18+
],
19+
"tools": ["A user-approved market data connector", "The GitHub repository connector"],
20+
"preferred_blocks": ["metric-group", "line-chart", "table", "notice"]
21+
}
22+
]
23+
}

docs/prompt-studio.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Prompt Studio
2+
3+
Prompt Studio turns a small, private local profile into a complete prompt that you can paste into ChatGPT, Claude, Gemini, a local model, or another scheduled workflow. The generated prompt includes repository locations, source intent, current Zaati OS contracts, safe presentation blocks, privacy boundaries, validation, retries, and atomic publication.
4+
5+
It does not connect to an LLM or store credentials.
6+
7+
## Three steps
8+
9+
```bash
10+
npm run prompt:create
11+
```
12+
13+
1. Choose one or more registered sources.
14+
2. Describe the information each source should contain, the approved tools it may use, and useful block types.
15+
3. Copy `.zaati/generated-prompts/<task>.scheduled-task.md` into your LLM and approve the connections it requests.
16+
17+
The profile and prompts are ignored by Git. Their directory uses mode `0700` and each file uses mode `0600`. They may still reveal repository names and workflow intent, so treat them as private configuration.
18+
19+
## Generate from a reusable profile
20+
21+
Start with the synthetic example:
22+
23+
```bash
24+
cp config/prompt-profile.example.json .zaati/market-pulse.json
25+
npm run prompt:create -- --config .zaati/market-pulse.json
26+
```
27+
28+
Use separate repositories by default:
29+
30+
- `code_repository` is the public Zaati OS fork that defines schemas, prompts, and renderers.
31+
- `data_repository` is the private repository that receives real snapshots.
32+
33+
The CLI rejects a shared code and data repository unless `allow_same_repository` is explicitly enabled. A public fork should never receive private snapshots.
34+
35+
## Profile contract
36+
37+
`schemas/prompt-profile.schema.json` is the executable contract. Each source needs:
38+
39+
- a registered `domain:source` ID
40+
- decision-oriented `requirements`
41+
- the exact approved `tools` available to the scheduled LLM
42+
- one or more `preferred_blocks` from the audited UI contract
43+
44+
The preferred block list is a safe menu, not a forced layout. The LLM should choose a line chart only for an ordered trend, a table for exact repeated fields, a calendar for timed events, and no visualization when plain text is clearer.
45+
46+
## One task, many snapshots
47+
48+
Add several source objects to the same profile. The generated prompt requires one complete `snapshot-bundle` result, validates all nested snapshots, retries the whole candidate at most three times, and publishes all dated files in one commit or pull request. It never publishes a valid subset from a failed run.
49+
50+
Direct sources should appear before aggregate sources. Registered aggregate dependencies still apply.
51+
52+
## Adding a source that does not exist
53+
54+
The interactive wizard intentionally accepts only registered sources. For a new source, add a `registration` object to a config profile. Prompt Studio then creates:
55+
56+
- `<task>.source-setup.md`, a one-time coding prompt that opens a public, generic pull request with synthetic fixtures and tests
57+
- `<task>.scheduled-task.md`, a recurring data-only prompt that refuses to run until the registration is merged
58+
59+
Keep these authorities separate. A scheduled task must never change application code, configuration, prompts, schemas, documentation, dependencies, or CI.
60+
61+
## Automation
62+
63+
```bash
64+
npm run prompt:create -- \
65+
--config .zaati/my-task.json \
66+
--output-dir .zaati/generated-prompts \
67+
--force
68+
```
69+
70+
Use `--stdout` only when you deliberately want the generated scheduled prompt in terminal output. By default, Prompt Studio prints paths rather than prompt contents to reduce accidental disclosure in logs.
71+
72+
## Before pasting
73+
74+
- Confirm the code URL points to the intended Zaati OS fork.
75+
- Confirm the data URL points to a private repository.
76+
- Give the LLM only the source and GitHub permissions it needs.
77+
- Review provider retention and training settings.
78+
- Keep Cloudflare Access in front of the deployed dashboard.
79+
- Run once with synthetic or low-sensitivity data before enabling the schedule.

docs/quickstart.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ npm run tutorial
1414

1515
The guided setup covers name, timezone, starter sources, visual style, and optional encryption. The tutorial proves validation and retry behavior with synthetic data, then opens the app.
1616

17+
When the demo feels right, generate the complete prompt for your real scheduled LLM task:
18+
19+
```bash
20+
npm run prompt:create
21+
```
22+
23+
Choose sources, describe the result you need, name the approved tools, and select useful presentation blocks. Paste the private generated prompt into your LLM. Read [Prompt Studio](prompt-studio.md) for multi-source profiles and new-source setup.
24+
1725
## 2. Configure the instance
1826

1927
The wizard writes `config/instance.local.json`. It is ignored by Git. You can rerun it with `npm run setup -- --force`.

docs/tutorials/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Start with one complete loop. Add nuance only after the first dashboard works.
88
4. [Make the design yours](theme-studio.md)
99

1010
For the credential-free guided demo, run `npm run tutorial`. The mock provider returns an invalid contract once on purpose, receives validation feedback, retries, and produces six synthetic snapshots. It is a tiny robot tripping over its shoelaces and then recovering professionally.
11+
12+
When you are ready to connect your own LLM, run `npm run prompt:create`. The [Prompt Studio guide](../prompt-studio.md) turns your repository URLs, source needs, tools, schedule, and preferred blocks into one copy-ready scheduled-task prompt.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"access:verify": "node scripts/verify-access.mjs",
3333
"instance:configure": "node scripts/configure-instance.mjs",
3434
"source:add": "node scripts/add-source.mjs",
35+
"prompt:create": "node scripts/create-prompt.mjs",
3536
"test": "node --test tests/*.test.mjs",
3637
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='scripts/lib/*.mjs' --test-coverage-include='scripts/run-workflow.mjs' --test-coverage-lines=90 --test-coverage-branches=78 --test-coverage-functions=80 tests/*.test.mjs",
3738
"deploy": "npm run check && npm run deployment:configure && wrangler deploy --config .wrangler.generated.jsonc"

schemas/prompt-profile.schema.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://zaati-os.dev/schemas/prompt-profile.schema.json",
4+
"title": "Zaati OS Prompt Studio profile",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"required": [
8+
"profile_version",
9+
"task_name",
10+
"code_repository",
11+
"data_repository",
12+
"provider",
13+
"timezone",
14+
"schedule",
15+
"publication",
16+
"sources"
17+
],
18+
"properties": {
19+
"profile_version": { "const": "0.1.1" },
20+
"task_name": { "type": "string", "minLength": 1, "maxLength": 100 },
21+
"code_repository": { "$ref": "#/$defs/repository" },
22+
"data_repository": { "$ref": "#/$defs/repository" },
23+
"provider": { "enum": ["chatgpt", "claude", "gemini", "local", "custom"] },
24+
"timezone": { "type": "string", "minLength": 1, "maxLength": 80 },
25+
"schedule": { "type": "string", "minLength": 1, "maxLength": 240 },
26+
"publication": { "enum": ["pull-request", "direct-commit"] },
27+
"allow_same_repository": { "type": "boolean", "default": false },
28+
"sources": {
29+
"type": "array",
30+
"minItems": 1,
31+
"maxItems": 20,
32+
"items": { "$ref": "#/$defs/source" }
33+
}
34+
},
35+
"$defs": {
36+
"repository": {
37+
"type": "string",
38+
"pattern": "^https://github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$",
39+
"maxLength": 240
40+
},
41+
"textList": {
42+
"type": "array",
43+
"minItems": 1,
44+
"maxItems": 30,
45+
"items": { "type": "string", "minLength": 1, "maxLength": 300 }
46+
},
47+
"source": {
48+
"type": "object",
49+
"additionalProperties": false,
50+
"required": ["id", "requirements", "tools", "preferred_blocks"],
51+
"properties": {
52+
"id": { "type": "string", "pattern": "^[a-z0-9-]+:[a-z0-9-]+$" },
53+
"requirements": { "$ref": "#/$defs/textList" },
54+
"tools": { "$ref": "#/$defs/textList" },
55+
"preferred_blocks": {
56+
"type": "array",
57+
"minItems": 1,
58+
"maxItems": 10,
59+
"uniqueItems": true,
60+
"items": {
61+
"enum": ["metric-group", "list", "line-chart", "bar-chart", "calendar", "table", "progress", "notice", "timeline", "text"]
62+
}
63+
},
64+
"registration": { "$ref": "#/$defs/registration" }
65+
}
66+
},
67+
"registration": {
68+
"type": "object",
69+
"additionalProperties": false,
70+
"required": ["label", "description", "authorized_inputs", "forbidden_inputs", "cadence", "freshness_sla_hours", "dashboard_role"],
71+
"properties": {
72+
"label": { "type": "string", "minLength": 1, "maxLength": 80 },
73+
"description": { "type": "string", "minLength": 1, "maxLength": 300 },
74+
"authorized_inputs": { "$ref": "#/$defs/textList" },
75+
"forbidden_inputs": { "$ref": "#/$defs/textList" },
76+
"cadence": { "enum": ["hourly", "daily", "weekdays", "weekly", "monthly", "manual"] },
77+
"freshness_sla_hours": { "type": "integer", "minimum": 1, "maximum": 8760 },
78+
"dashboard_role": { "enum": ["primary", "supporting", "hidden"] },
79+
"depends_on": {
80+
"type": "array",
81+
"maxItems": 20,
82+
"uniqueItems": true,
83+
"items": { "type": "string", "pattern": "^[a-z0-9-]+:[a-z0-9-]+$" },
84+
"default": []
85+
}
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)