Skip to content

Commit e4dca56

Browse files
authored
Merge pull request #559 from objectstack-ai/claude/copilots-undefined-tools-1n2wn1
docs: stop advertising src/agents and src/cubes, guard the tree in CI
2 parents 8a097bf + 72d56d2 commit e4dca56

8 files changed

Lines changed: 116 additions & 23 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'hotcrm': patch
3+
---
4+
5+
Stop documenting directories that no longer exist, and guard the class. `#512`
6+
deleted `src/agents/` when the AI surface went skills-only, but seven maintainer
7+
docs kept printing `src/agents/*.agent.ts` in their tree diagrams and
8+
registration tables — `code_examples.md` still told authors to "add its name to
9+
an agent in `src/agents/*.agent.ts`" after registering a skill. `src/cubes/` had
10+
the same shape: dropped in favour of datasets (ADR-0021, noted in
11+
`objectstack.config.ts`), still drawn in two trees. Also removes the skill
12+
`permissions: [...]` key from the worked example — `SkillSchema` has no such
13+
field and silently strips it (#511) — and corrects a stale flow count (20 → 23)
14+
and the `*.action.ts` suffix (the convention is `*.actions.ts`).
15+
16+
Fills the gap left behind: the skill example now states which two sources a
17+
`tools` name can resolve to (platform data tools, or `action_<name>` from an
18+
`ai.exposed` Action), why `defineTool` is not a third one, and where the guard
19+
lives. `ARCHITECTURE.md` gains the same note plus the missing `case_triage`
20+
skill.
21+
22+
Adds a repo-tree guard to `test/docs-drift.test.ts`: every `src/<dir>/` path a
23+
maintainer doc names must exist on disk (`docs/archive/` excluded — it is a
24+
historical record). It caught a stray reference in this change's own first pass.

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ hotcrm/
3535
│ ├── objects/ # *.object.ts schemas + *.hook.ts lifecycle hooks
3636
│ ├── views/ pages/ # App UI metadata (*.view.ts / *.page.ts)
3737
│ ├── flows/ # Automation (*.flow.ts)
38-
│ ├── actions/ # UI actions + AI-callable tools (*.action.ts)
39-
│ ├── dashboards/ reports/ datasets/ cubes/ # Analytics metadata
40-
│ ├── agents/ skills/ # AI agent + skill metadata
38+
│ ├── actions/ # UI actions + AI-callable tools (*.actions.ts)
39+
│ ├── dashboards/ reports/ datasets/ # Analytics metadata
40+
│ ├── skills/ # AI skill metadata (*.skill.ts) — skills-only surface
4141
│ ├── profiles/ sharing/ # Permission sets, role hierarchy, sharing rules
4242
│ ├── translations/ # Locale bundles (en / zh-CN / es-ES / ja-JP)
4343
│ └── data/ # Seed data (defineDataset)

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ hotcrm/
126126
├── src/
127127
│ ├── objects/ # *.object.ts — data model (15 objects)
128128
│ ├── actions/ # *.actions.ts — server actions + AI tools (13)
129-
│ ├── flows/ # *.flow.ts — visual flows (20): screen, record-change & scheduled
129+
│ ├── flows/ # *.flow.ts — visual flows (23): screen, record-change & scheduled
130130
│ ├── hooks/ # hook registry barrel
131-
│ ├── agents/ # *.agent.ts — AI copilots (2)
132-
│ ├── skills/ # *.skill.ts — AI skills (6)
133-
│ ├── cubes/ # *.cube.ts — analytics cubes
131+
│ ├── skills/ # *.skill.ts — AI skills (6) — skills-only surface, no agents
132+
│ ├── datasets/ # *.dataset.ts — analytics semantic layer (8)
134133
│ ├── dashboards/, reports/ # analytics UI
135134
│ ├── pages/, views/, apps/ # UI definitions
136135
│ ├── profiles/, sharing/ # security

docs/ARCHITECTURE.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ Runtime capabilities are declared in `requires`: `ai`, `automation`, `triggers`,
4545
| Lifecycle hooks | `src/objects/*.hook.ts` via `src/hooks/index.ts` | `hooks` |
4646
| UI actions | `src/actions/*.actions.ts` | `actions` |
4747
| Automation | `src/flows/*.flow.ts` | `flows` |
48-
| AI agents | `src/agents/*.agent.ts` | `agents` |
4948
| AI skills | `src/skills/*.skill.ts` | `skills` |
5049
| Apps, views, pages | `src/apps/`, `src/views/`, `src/pages/` | `apps`, `views`, `pages` |
51-
| Analytics | `src/cubes/`, `src/dashboards/`, `src/reports/` | `analyticsCubes`, `dashboards`, `reports` |
50+
| Analytics | `src/datasets/`, `src/dashboards/`, `src/reports/` | `datasets`, `dashboards`, `reports` |
5251
| Security | `src/profiles/`, `src/sharing/` | `permissions`, `sharingRules`, `roles` |
5352
| i18n | `src/translations/` | `translations`, `i18n` |
5453
| Demo data | `src/data/` | `data` |
@@ -127,15 +126,24 @@ The UI is metadata-driven. Object field definitions, views, pages, actions, perm
127126

128127
## AI
129128

130-
AI is modeled as ObjectStack metadata:
129+
AI is modeled as ObjectStack metadata. The surface is **skills-only**: the two
130+
`*.agent.ts` copilots were retired in [#512](https://github.com/objectstack-ai/hotcrm/pull/512),
131+
because ADR-0063 §2 closed agent records to third parties and the runtime
132+
refuses non-platform ones. The capability lives in the skills, which attach to
133+
the platform assistant by surface affinity.
131134

132135
| Layer | Files | Examples |
133136
| --- | --- | --- |
134-
| Agents | `src/agents/*.agent.ts` | `sales_copilot`, `service_copilot` |
135-
| Skills | `src/skills/*.skill.ts` | `live_data`, `lead_qualification`, `email_drafting`, `revenue_forecasting`, `customer_360` |
137+
| Skills | `src/skills/*.skill.ts` | `live_data`, `lead_qualification`, `email_drafting`, `revenue_forecasting`, `case_triage`, `customer_360` |
136138
| Actions and flows | `src/actions/`, `src/flows/` | lead conversion, case triage, alerts |
137139

138-
The Sales Copilot instructions explicitly require live schema inspection before answering record questions, because admins can change metadata over time.
140+
Skills declare no bespoke tools (ADR-0109). They compose the platform's data
141+
tools with the `action_<name>` tools the runtime materialises from Actions that
142+
opt in via `ai.exposed` (ADR-0011) — every name a skill declares must resolve to
143+
one of those, which `test/skills-integrity.test.ts` enforces.
144+
145+
The `live_data` skill explicitly requires live schema inspection before
146+
answering record questions, because admins can change metadata over time.
139147

140148
## Security
141149

docs/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ hotcrm/
3737
│ ├── objects/ # ObjectSchema.create metadata, object lifecycle hooks
3838
│ ├── actions/ # UI actions and AI-callable action bodies
3939
│ ├── flows/ # ObjectStack automation flows
40-
│ ├── agents/ # AI agent definitions
41-
│ ├── skills/ # AI skills used by agents
40+
│ ├── skills/ # AI skill definitions (skills-only surface)
4241
│ ├── dashboards/ # Dashboard metadata
4342
│ ├── reports/ # Report metadata
4443
│ ├── views/, pages/ # App UI metadata

docs/STATUS.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ pnpm verify
5656
| Object hooks | `src/objects/*.hook.ts`, collected by `src/hooks/index.ts` |
5757
| Actions | `src/actions/*.actions.ts` |
5858
| Flows | `src/flows/*.flow.ts` |
59-
| Agents | `src/agents/*.agent.ts` |
60-
| Skills | `src/skills/*.skill.ts` |
59+
| Skills | `src/skills/*.skill.ts` (skills-only AI surface since #512 — the agent directory is gone) |
6160
| Views and pages | `src/views/`, `src/pages/` |
6261
| Dashboards and reports | `src/dashboards/`, `src/reports/` |
6362
| Security | `src/profiles/`, `src/sharing/` |

docs/developers/code_examples.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> Examples that match the current single-app HotCRM repository.
44
5-
HotCRM metadata is registered from `src/` through [`objectstack.config.ts`](../../objectstack.config.ts). File names use the ObjectStack suffix convention: `.object.ts`, `.hook.ts`, `.actions.ts`, `.flow.ts`, `.agent.ts`, `.skill.ts`, `.view.ts`, `.page.ts`, `.dashboard.ts`, and `.report.ts`.
5+
HotCRM metadata is registered from `src/` through [`objectstack.config.ts`](../../objectstack.config.ts). File names use the ObjectStack suffix convention: `.object.ts`, `.hook.ts`, `.actions.ts`, `.flow.ts`, `.skill.ts`, `.view.ts`, `.page.ts`, `.dashboard.ts`, and `.report.ts`.
66

77
## Define An Object
88

@@ -188,7 +188,7 @@ export const WarrantyExpirationFlow: Flow = {
188188

189189
Export flows from `src/flows/index.ts`. Record-change flows require the `triggers` capability, which is already declared in `objectstack.config.ts`.
190190

191-
## Add An Agent Skill
191+
## Add An AI Skill
192192

193193
File: `src/skills/example.skill.ts`
194194

@@ -204,11 +204,34 @@ summarize status, identify expired or soon-expiring warranties, and
204204
recommend the next action.`,
205205
tools: ['describe_object', 'query_records'],
206206
triggerPhrases: ['summarize warranties', 'warranty risk'],
207-
permissions: ['crm:account:read'],
208207
});
209208
```
210209

211-
Register the skill in `src/skills/index.ts`, then add its name to an agent in `src/agents/*.agent.ts` if the agent should use it.
210+
Register the skill in `src/skills/index.ts`. There is no agent to attach it to —
211+
the AI surface is skills-only ([#512](https://github.com/objectstack-ai/hotcrm/pull/512)),
212+
and a skill binds to the platform assistant through its `surface` affinity.
213+
214+
**Every name in `tools` must resolve to something that actually runs.** The
215+
runtime silently drops a tool it cannot resolve, so an invented name leaves the
216+
model with instructions describing a capability it does not have — the defect
217+
[#493](https://github.com/objectstack-ai/hotcrm/issues/493) catalogued. Two
218+
sources resolve, and only two:
219+
220+
- **Platform data tools**`describe_object`, `list_objects`, `query_records`,
221+
`get_record`, `aggregate_data`.
222+
- **`action_<name>`** — materialised from an Action that opts in with
223+
`ai: { exposed: true, description }` (ADR-0011, default off) *and* has a
224+
headless path. See `ConvertLeadAction` in `src/actions/lead.actions.ts`.
225+
226+
Authoring a `defineTool` record does **not** create a third source: `ToolSchema`
227+
is a read-only projection for Studio discovery with no `implementation` field
228+
and no executor. Reasoning — scoring, drafting, forecasting — belongs in
229+
`instructions`, not in a tool (ADR-0109). `test/skills-integrity.test.ts`
230+
enforces all of this at PR time.
231+
232+
Note there is no `permissions` key on a skill — `SkillSchema` has no such field,
233+
so one is silently stripped ([#511](https://github.com/objectstack-ai/hotcrm/pull/511)).
234+
Gate access on the Actions the skill calls instead.
212235

213236
## Common Registration Points
214237

@@ -218,7 +241,6 @@ Register the skill in `src/skills/index.ts`, then add its name to an agent in `s
218241
| Hook | `src/objects/` | `src/hooks/index.ts` |
219242
| Action | `src/actions/` | `src/actions/index.ts` |
220243
| Flow | `src/flows/` | `src/flows/index.ts` |
221-
| Agent | `src/agents/` | `src/agents/index.ts` |
222244
| Skill | `src/skills/` | `src/skills/index.ts` |
223245
| View | `src/views/` | `src/views/index.ts` |
224246
| Page | `src/pages/` | `src/pages/index.ts` |

test/docs-drift.test.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
22

33
import { describe, it, expect } from 'vitest';
4-
import { readFileSync } from 'node:fs';
4+
import { existsSync, readFileSync } from 'node:fs';
55
import { join } from 'node:path';
66

77
/**
@@ -143,3 +143,45 @@ describe('package docs do not drift from the flows they document', () => {
143143
});
144144
}
145145
});
146+
147+
/**
148+
* Repo-tree drift — a doc must not advertise a directory that is gone.
149+
*
150+
* `src/agents/` outlived its deletion by three PRs. #512 removed the two
151+
* copilots and the whole directory, but seven maintainer docs kept printing
152+
* `src/agents/*.agent.ts` in their tree diagrams and registration tables, so
153+
* the next reader (human or agent) was told to put a file somewhere that does
154+
* not exist. `src/cubes/` had the same shape: dropped in favour of datasets
155+
* (ADR-0021, see the note in objectstack.config.ts), still drawn in two trees.
156+
*
157+
* Nothing checked, because a path in prose is just prose. This walks the
158+
* maintainer docs, pulls every `src/<dir>/` they mention, and resolves it
159+
* against the real tree. `docs/archive/` is deliberately excluded — it is a
160+
* historical record and is allowed to describe a repo that no longer exists.
161+
*/
162+
const TREE_DOCS = [
163+
'README.md',
164+
'AGENTS.md',
165+
'docs/README.md',
166+
'docs/STATUS.md',
167+
'docs/ARCHITECTURE.md',
168+
'docs/MAINTENANCE.md',
169+
'docs/DEPLOYMENT.md',
170+
'docs/developers/code_examples.md',
171+
'docs/developers/api_reference.md',
172+
];
173+
174+
describe('maintainer docs do not point at directories that no longer exist', () => {
175+
for (const docFile of TREE_DOCS) {
176+
it(`${docFile}: every src/<dir>/ it names exists`, () => {
177+
const text = readFileSync(join(docFile), 'utf8');
178+
const named = new Set([...text.matchAll(/\bsrc\/([a-z][a-z0-9_]*)\//g)].map((m) => m[1]));
179+
const missing = [...named].filter((dir) => !existsSync(join('src', dir)));
180+
expect(
181+
missing,
182+
`${docFile} advertises src/ directories that do not exist: ${missing.join(', ')}. ` +
183+
'Delete the reference (or restore the directory) — a path in prose is still a promise.',
184+
).toEqual([]);
185+
});
186+
}
187+
});

0 commit comments

Comments
 (0)