Skip to content

Commit 07c1e2a

Browse files
authored
Merge pull request #10 from mohsinht/ci/code-quality-guardrails
ci: enforce code quality guardrails
2 parents e6266bc + b4b5915 commit 07c1e2a

62 files changed

Lines changed: 4283 additions & 520 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,77 @@ concurrency:
1414
cancel-in-progress: true
1515

1616
jobs:
17-
validate:
17+
static-contracts:
18+
name: Static analysis and contracts
1819
runs-on: ubuntu-24.04
19-
timeout-minutes: 15
20+
timeout-minutes: 10
21+
steps:
22+
- uses: actions/checkout@v7
23+
with:
24+
persist-credentials: false
25+
- uses: actions/setup-node@v7
26+
with:
27+
node-version: 22
28+
cache: npm
29+
- run: npm ci
30+
- run: npm run format:check
31+
- run: npm run lint
32+
- run: npm run typecheck
33+
- run: npm run repository:validate
34+
- run: npm run contracts:check
35+
36+
unit-coverage:
37+
name: Unit tests and coverage
38+
runs-on: ubuntu-24.04
39+
timeout-minutes: 10
40+
steps:
41+
- uses: actions/checkout@v7
42+
with:
43+
persist-credentials: false
44+
- uses: actions/setup-node@v7
45+
with:
46+
node-version: 22
47+
cache: npm
48+
- run: npm ci
49+
- run: npm run test:coverage
50+
- run: npm run tutorial:check
51+
52+
build-performance:
53+
name: Production build and budgets
54+
runs-on: ubuntu-24.04
55+
timeout-minutes: 10
56+
steps:
57+
- uses: actions/checkout@v7
58+
with:
59+
persist-credentials: false
60+
- uses: actions/setup-node@v7
61+
with:
62+
node-version: 22
63+
cache: npm
64+
- run: npm ci
65+
- run: npm run build
66+
- run: npm run performance:check
67+
68+
accessibility:
69+
name: Accessibility matrix
70+
runs-on: ubuntu-24.04
71+
timeout-minutes: 10
72+
steps:
73+
- uses: actions/checkout@v7
74+
with:
75+
persist-credentials: false
76+
- uses: actions/setup-node@v7
77+
with:
78+
node-version: 22
79+
cache: npm
80+
- run: npm ci
81+
- run: npm run build
82+
- run: npm run accessibility:check
83+
84+
security-audit:
85+
name: Dependency security audit
86+
runs-on: ubuntu-24.04
87+
timeout-minutes: 10
2088
steps:
2189
- uses: actions/checkout@v7
2290
with:
@@ -26,5 +94,37 @@ jobs:
2694
node-version: 22
2795
cache: npm
2896
- run: npm ci
29-
- run: npm run check
3097
- run: npm audit --audit-level=high
98+
99+
quality-gate:
100+
name: Quality gate
101+
if: always()
102+
needs: [static-contracts, unit-coverage, build-performance, accessibility, security-audit]
103+
runs-on: ubuntu-24.04
104+
timeout-minutes: 5
105+
steps:
106+
- name: Require every quality dimension
107+
env:
108+
ACCESSIBILITY_RESULT: ${{ needs.accessibility.result }}
109+
BUILD_RESULT: ${{ needs['build-performance'].result }}
110+
SECURITY_RESULT: ${{ needs['security-audit'].result }}
111+
STATIC_RESULT: ${{ needs['static-contracts'].result }}
112+
UNIT_RESULT: ${{ needs['unit-coverage'].result }}
113+
run: |
114+
failed=0
115+
for result in "$STATIC_RESULT" "$UNIT_RESULT" "$BUILD_RESULT" "$ACCESSIBILITY_RESULT" "$SECURITY_RESULT"; do
116+
if [ "$result" != "success" ]; then
117+
failed=1
118+
fi
119+
done
120+
if [ "$failed" -ne 0 ]; then
121+
echo "One or more required quality jobs did not pass."
122+
exit 1
123+
fi
124+
- name: Publish quality summary
125+
run: |
126+
{
127+
echo "## Zaati OS quality gate"
128+
echo
129+
echo "All required formatting, static analysis, contract, coverage, build, performance, accessibility, and dependency audit jobs passed."
130+
} >> "$GITHUB_STEP_SUMMARY"

.prettierignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
dist/
3+
.zaati/
4+
public/data/dashboard-data.json
5+
config/instance.local.json
6+
package-lock.json
7+
docs/assets/
8+
*.tsbuildinfo

.prettierrc.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"printWidth": 140,
3+
"semi": false,
4+
"singleQuote": false,
5+
"trailingComma": "all",
6+
"overrides": [
7+
{
8+
"files": "*.jsonc",
9+
"options": {
10+
"trailingComma": "none"
11+
}
12+
}
13+
]
14+
}

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
1313
- Private data-repository workflow and local ignored snapshot mode.
1414
- Cloudflare Workers deployment with Access-first guidance and public preview routes disabled.
1515
- Validation, privacy scanning, tests, CI, CodeQL, Dependabot, release, and optional deployment workflows.
16+
- Type-aware ESLint, deterministic Prettier formatting, repository policy checks, and independently visible CI quality jobs.
17+
- Enforced core coverage floors of 90% lines, 78% branches, and 80% functions, backed by command-adapter and security edge-case tests.
1618
- Configurable palettes, display density, radius, brand label, locale, and timezone.
1719
- Three-step onboarding, an in-app tutorial, Make targets, and a credential-free mock provider.
1820
- Atomic multi-snapshot bundles, safe validation feedback, and bounded workflow retries.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Zaati OS welcomes focused improvements that help users notice, decide, or act wh
88
2. For substantial architecture or contract changes, open a proposal before implementation.
99
3. Fork the repository and create a descriptive branch.
1010
4. Keep all fixtures synthetic.
11-
5. Run `npm ci` and `npm run check`.
11+
5. Run `npm ci` and `npm run check`. Formatting, lint, repository policy, coverage, performance, privacy, and accessibility failures are blocking.
1212
6. Open a pull request using the template.
1313

1414
## Domain packs

README.md

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Zaati OS
22

3+
[![CI](../../actions/workflows/ci.yml/badge.svg?branch=main)](../../actions/workflows/ci.yml)
4+
[![CodeQL](../../actions/workflows/codeql.yml/badge.svg?branch=main)](../../actions/workflows/codeql.yml)
5+
36
**Your life, organized by the AI you already use.**
47

58
Zaati OS is an open-source, private-by-default personal operating system. Scheduled AI workflows turn approved sources into versioned snapshots, and a schema-driven dashboard turns those snapshots into useful daily views, trends, calendars, lists, tables, and reviews.
@@ -92,9 +95,7 @@ The common envelope records source identity, time period, producer, status, prov
9295
"summary": "Two focus blocks and one decision need attention.",
9396
"presentation": {
9497
"layout": "dashboard",
95-
"blocks": [
96-
{ "id": "day", "kind": "calendar", "title": "Today", "date": "2030-01-15", "events": [] }
97-
]
98+
"blocks": [{ "id": "day", "kind": "calendar", "title": "Today", "date": "2030-01-15", "events": [] }]
9899
}
99100
}
100101
}
@@ -104,15 +105,15 @@ See [LLM contract](docs/llm-contract.md) and [`schemas/`](schemas/) for the exec
104105

105106
## Included starter workflows
106107

107-
| Prompt | Purpose | Default visualization |
108-
| --- | --- | --- |
109-
| `inbox-attention.md` | Extract only messages needing a decision or response | Prioritized list |
110-
| `daily-agenda.md` | Turn calendars and tasks into a realistic day | Calendar and action list |
111-
| `work-focus.md` | Surface owned work, blockers, and next actions | Status metrics and table |
112-
| `money-pulse.md` | Normalize user-approved financial summaries | Metrics, line chart, notices |
113-
| `news-briefing.md` | Keep only high-value developments | Evidence-linked list |
114-
| `daily-overview.md` | Combine registered source snapshots | Adaptive dashboard |
115-
| `weekly-review.md` | Find patterns and produce an evidence-based review | Progress, timeline, decisions |
108+
| Prompt | Purpose | Default visualization |
109+
| -------------------- | ---------------------------------------------------- | ----------------------------- |
110+
| `inbox-attention.md` | Extract only messages needing a decision or response | Prioritized list |
111+
| `daily-agenda.md` | Turn calendars and tasks into a realistic day | Calendar and action list |
112+
| `work-focus.md` | Surface owned work, blockers, and next actions | Status metrics and table |
113+
| `money-pulse.md` | Normalize user-approved financial summaries | Metrics, line chart, notices |
114+
| `news-briefing.md` | Keep only high-value developments | Evidence-linked list |
115+
| `daily-overview.md` | Combine registered source snapshots | Adaptive dashboard |
116+
| `weekly-review.md` | Find patterns and produce an evidence-based review | Progress, timeline, decisions |
116117

117118
These are provider-neutral templates. Copy one into any tool that can read approved sources and write JSON to the private snapshot store.
118119

@@ -144,20 +145,40 @@ Read [Privacy and threat model](docs/privacy.md) before connecting a real source
144145

145146
## Commands
146147

147-
| Command | Result |
148-
| --- | --- |
149-
| `npm run dev` | Build the data index and start Vite |
150-
| `npm run setup` | Complete the guided three-step local setup |
151-
| `npm run tutorial` | Run the retrying mock LLM bundle and open it locally |
152-
| `npm run workflow:run` | Connect any command-based LLM adapter |
153-
| `npm run snapshot:ingest` | Atomically validate and persist one multi-snapshot bundle |
154-
| `npm run snapshot:keygen` | Create an ignored 256-bit snapshot key |
155-
| `npm run instance:configure` | Create ignored local settings |
156-
| `npm run source:add` | Scaffold a source catalog entry and worker prompt |
157-
| `npm run data:validate` | Validate registries, snapshots, ownership, and UI blocks |
158-
| `npm run privacy:validate` | Reject private paths and common credential shapes |
159-
| `npm run check` | Run contracts, security, build, retry, encryption, performance, and WCAG tests |
160-
| `npm run deploy` | Validate, build, and deploy with Wrangler |
148+
| Command | Result |
149+
| ---------------------------- | ------------------------------------------------------------------------------ |
150+
| `npm run dev` | Build the data index and start Vite |
151+
| `npm run setup` | Complete the guided three-step local setup |
152+
| `npm run tutorial` | Run the retrying mock LLM bundle and open it locally |
153+
| `npm run workflow:run` | Connect any command-based LLM adapter |
154+
| `npm run snapshot:ingest` | Atomically validate and persist one multi-snapshot bundle |
155+
| `npm run snapshot:keygen` | Create an ignored 256-bit snapshot key |
156+
| `npm run instance:configure` | Create ignored local settings |
157+
| `npm run source:add` | Scaffold a source catalog entry and worker prompt |
158+
| `npm run data:validate` | Validate registries, snapshots, ownership, and UI blocks |
159+
| `npm run privacy:validate` | Reject private paths and common credential shapes |
160+
| `npm run format:check` | Reject formatting drift with Prettier |
161+
| `npm run lint` | Run type-aware ESLint, React Hooks, and React Refresh rules |
162+
| `npm run test:coverage` | Run tests with enforced line, branch, and function coverage |
163+
| `npm run check` | Run contracts, security, build, retry, encryption, performance, and WCAG tests |
164+
| `npm run deploy` | Validate, build, and deploy with Wrangler |
165+
166+
## Enforced quality gates
167+
168+
The badges at the top of this README reflect the current default-branch CI and CodeQL results. A red badge means the published branch is failing a real check, not that someone forgot to update a status table.
169+
170+
| Gate | Enforced standard |
171+
| --------------------- | ------------------------------------------------------------------------------------------------------------------ |
172+
| Formatting | Zero Prettier drift |
173+
| Static analysis | Zero ESLint errors or warnings, strict TypeScript build |
174+
| Repository policy | Exact dependency versions, valid workflow YAML, timeouts, least-privilege permissions, safe checkout configuration |
175+
| Contracts and privacy | Every registry, snapshot, schema, ownership rule, deployment boundary, and committed path validates |
176+
| Unit coverage | At least 90% lines, 78% branches, and 80% functions across the ingestion and encryption core |
177+
| Performance | At most 120 KB JavaScript gzip, 20 KB CSS gzip, and 120 KB dashboard data gzip |
178+
| Accessibility | Zero axe WCAG A or AA violation groups across tutorial and dashboard, light, dark, desktop, and mobile |
179+
| Security | Zero high-severity npm audit findings plus CodeQL analysis |
180+
181+
Pull requests expose each gate as a separate job and finish with one `Quality gate` result suitable for branch protection. Run `npm run check` locally for the same product checks before pushing.
161182

162183
## Deployment choices
163184

config/instance.example.json

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,7 @@
77
"locale": "en-US",
88
"currency": "USD",
99
"week_starts_on": "monday",
10-
"enabled_sources": [
11-
"overview:daily",
12-
"agenda:primary",
13-
"inbox:attention",
14-
"work:focus",
15-
"money:pulse",
16-
"news:briefing",
17-
"review:weekly"
18-
],
10+
"enabled_sources": ["overview:daily", "agenda:primary", "inbox:attention", "work:focus", "money:pulse", "news:briefing", "review:weekly"],
1911
"theme": {
2012
"preset": "sage",
2113
"default_mode": "system",

config/workflows.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,7 @@
77
"label": "Daily core bundle",
88
"description": "Refresh the primary daily sources and build the overview in one validated run.",
99
"prompt": "prompts/daily-bundle.md",
10-
"source_ids": [
11-
"agenda:primary",
12-
"inbox:attention",
13-
"work:focus",
14-
"money:pulse",
15-
"news:briefing",
16-
"overview:daily"
17-
],
10+
"source_ids": ["agenda:primary", "inbox:attention", "work:focus", "money:pulse", "news:briefing", "overview:daily"],
1811
"max_attempts": 3,
1912
"publication": "single-commit"
2013
}

data/examples/agenda/primary/2026-08-24.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@
2727
"date": "2026-08-24",
2828
"span": "two",
2929
"events": [
30-
{ "id": "a1", "title": "Build the first usable slice", "start": "2026-08-24T08:30:00Z", "end": "2026-08-24T10:30:00Z", "tone": "positive" },
30+
{
31+
"id": "a1",
32+
"title": "Build the first usable slice",
33+
"start": "2026-08-24T08:30:00Z",
34+
"end": "2026-08-24T10:30:00Z",
35+
"tone": "positive"
36+
},
3137
{ "id": "a2", "title": "Project sync", "start": "2026-08-24T11:00:00Z", "end": "2026-08-24T11:30:00Z", "tone": "info" },
3238
{ "id": "a3", "title": "Review and ship", "start": "2026-08-24T13:00:00Z", "end": "2026-08-24T14:30:00Z", "tone": "positive" }
3339
]

data/examples/inbox/attention/2026-08-24.json

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,49 @@
2020
"presentation": {
2121
"layout": "focus",
2222
"blocks": [
23-
{ "id": "counts", "kind": "metric-group", "title": "Attention filter", "span": "full", "metrics": [{ "label": "Needs reply", "value": 2, "tone": "warning" }, { "label": "Waiting on others", "value": 1, "tone": "neutral" }, { "label": "Ignored noise", "value": 18, "tone": "positive" }] },
23+
{
24+
"id": "counts",
25+
"kind": "metric-group",
26+
"title": "Attention filter",
27+
"span": "full",
28+
"metrics": [
29+
{ "label": "Needs reply", "value": 2, "tone": "warning" },
30+
{ "label": "Waiting on others", "value": 1, "tone": "neutral" },
31+
{ "label": "Ignored noise", "value": 18, "tone": "positive" }
32+
]
33+
},
2434
{
2535
"id": "messages",
2636
"kind": "list",
2737
"title": "Reply queue",
2838
"span": "two",
2939
"items": [
30-
{ "id": "m1", "title": "Choose a planning window", "description": "A collaborator proposed two times for tomorrow.", "meta": "Due today", "status": "Decision", "tone": "warning" },
31-
{ "id": "m2", "title": "Approve the revised outline", "description": "The requested changes are ready for a quick review.", "meta": "15 minutes", "status": "Review", "tone": "info" }
40+
{
41+
"id": "m1",
42+
"title": "Choose a planning window",
43+
"description": "A collaborator proposed two times for tomorrow.",
44+
"meta": "Due today",
45+
"status": "Decision",
46+
"tone": "warning"
47+
},
48+
{
49+
"id": "m2",
50+
"title": "Approve the revised outline",
51+
"description": "The requested changes are ready for a quick review.",
52+
"meta": "15 minutes",
53+
"status": "Review",
54+
"tone": "info"
55+
}
3256
]
3357
},
34-
{ "id": "noise", "kind": "notice", "title": "Noise stayed quiet", "body": "Newsletters, receipts, automated updates, and promotional messages were excluded from the attention queue.", "tone": "positive", "span": "one" }
58+
{
59+
"id": "noise",
60+
"kind": "notice",
61+
"title": "Noise stayed quiet",
62+
"body": "Newsletters, receipts, automated updates, and promotional messages were excluded from the attention queue.",
63+
"tone": "positive",
64+
"span": "one"
65+
}
3566
]
3667
}
3768
}

0 commit comments

Comments
 (0)