Set up AI agent instruction files for a workspace
What you'll see
An AI coding agent opens a Docly workspace and treats it like a conventional project: runs git init, hunts for package.json, writes a new .docly straight to the drive and burns the filename, reaches for Express-style req/res in an endpoint, hardcodes leading-slash paths.
The reflex is to write a long CLAUDE.md listing every rule the agent got wrong. That file grows into a second, worse copy of the documentation — one that nobody maintains, that contradicts the real pages within a month, and that still misses whatever the agent runs into next.
What's actually happening
The instruction file is not where platform knowledge should live. Docly already has developer documentation and a knowledge base; both are maintained, both are public, both are more complete than anything you will retype into a markdown file. An agent that has been handed a summary will trust the summary and stop reading. An agent that has been handed a map will go and read the actual page.
So the file has exactly three jobs:
1. Make the knowledge base mandatory. Every KB entry exists because someone already got that thing wrong — a burned filename, one visitor's session data cached and served to everyone, a live site down for forty minutes. Reading it before starting is what prevents the repeat. Reading it again afterwards is what catches the mistake while it is still cheap. This has to be stated as an instruction, not a suggestion, or it gets skipped.
2. Map the documentation. An agent cannot look up what it does not know exists. It will not guess that docly.linkImage exists, or that #/Services.json drives scheduled jobs, or that git is supported through a credential helper. A table of section links solves that at a fraction of the size of explaining any of it.
3. Carry only what is genuinely not lookupable. Which workspace this is — production or test, mapped drive or git clone — is nowhere in the documentation, because it differs per copy. That, and a pointer to everything else.
Two mechanical facts shape where the files go. The workspace root is a published folder, so a CLAUDE.md or AGENTS.md placed there returns 200 and is downloadable by anyone; .claude/ returns 404, and Claude Code reads ./.claude/CLAUDE.md as project instructions just as happily as ./CLAUDE.md. And @-imports are not lazy — an imported file is inlined at session start, every session, so splitting a long file into several buys maintenance, not context. The mechanism that actually defers loading is a skill.
What to do
The files
<workspace root>/
├── .claude/
│ ├── CLAUDE.md ← which workspace this is, and its local conventions
│ ├── docly.md ← the KB mandate + the documentation map (imported)
│ └── skills/ ← long procedures, loaded only when relevant
└── <your content> ← all of this is served over HTTP Import the map from CLAUDE.md with a relative path — @docly.md. Keep CLAUDE.md under about 120 lines and docly.md mostly links.
Verify once per workspace that nothing leaked to the root. Both of these must return 404:
curl -s -o /dev/null -w "%{http_code}\n" https://<your-site>/CLAUDE.md
curl -s -o /dev/null -w "%{http_code}\n" https://<your-site>/AGENTS.md 1. Open with the knowledge base — as an instruction
Before you start a task, read the knowledge base entries covering what you are about to touch. After you finish, check it again against what you actually did. If you discover something the KB does not cover, add an entry — that is the point of it.
Follow it with a lookup table so the agent knows which entries apply to the task in front of it, rather than being told to read twenty-nine articles:
| Read before you… | Entry |
|---|---|
| Touch anything on a live site | Work safely on a live site |
| Write any server-side JavaScript | Docly JavaScript is not Node |
| Reach for git | The mapped drive is not a git working copy |
| Work over the mapped drive | The site drive is WebDAV not a disk, False write-verification failures |
Create a .docly document | Creating new docly files |
Write a .hash page | Hash files are cached, Use master pages for shared layout |
| Write paths or links in JS | Use tilde paths in JavaScript, Omit file extensions in links |
| Build an API endpoint | Reading query and form fields, Group API endpoints into topic folders |
| Store or expose data | Keep private data under the # folder, Path traversal |
| Add or link an image | Scale and convert images, Do not build URLs by hand, Restrict and register image sizes |
| Ship static assets | Cache-busting bundles with assetUrl |
| Verify a platform behaviour | Use a scratch hash file to test Docly functions |
Never report documented behaviour as a problem
The mandate above has a second half that matters just as much as the reading: an agent must not raise documented platform behaviour with the user as a bug, a risk, or something to "be aware of". This is not a harmless extra. It sends the user off to investigate something the platform settled long ago, buries the real findings in noise, and costs precisely the time the knowledge base exists to save. On a security review it is worse than noise — it puts remediation work on the roadmap for a vulnerability class that does not exist.
State it in the instruction file as an instruction, not a preference:
Before reporting a bug, a vulnerability or a platform limitation, find the KB entry that covers it. If the behaviour is documented, it is not a finding — do not raise it, and do not soften it into a hedge ("if Docly resolves this…", "worth verifying…", "potential risk"). A hedged false alarm costs the user the same investigation as a confident one. If you have already raised it, correct it in one sentence and carry on.
The two that recur most:
- Path traversal. The filesystem API accepts absolute paths only and rejects
../at the boundary, so concatenating user input into a path is not an exploitable traversal. See Path traversal. - "Write verification failed". A false alarm from the client's own size check on a WebDAV mount, never data loss. See False write-verification failures. Reporting it as possible corruption invites exactly the panicked re-write that has taken live sites down.
The rule cuts both ways. Behaviour that genuinely is not documented is a finding, and once resolved it belongs in a KB entry — not in the workspace instruction file.
2. Map the documentation
This is the bulk of the file. Copy it as-is — it is the same in every Docly workspace.
| Page | For |
|---|---|
| Introduction, Getting Started | What Docly is: the file system, templates, the first API |
| JavaScript reference | The index for everything below |
| About JavaScript, HASH files | How HashJS differs from Node; #expr# and #{ }# syntax |
| Filesystem functions | getFile, getFiles, saveJson, linkImage, linkFile |
| Output functions | write, setMime, setHeader, assetUrl, redirect, htmlToPdf |
| Network, Mail, Share, Authentication | HTTP calls, SMTP/POP3/IMAP, folder shares, JWT and access control |
| String, Array, Math, Date and time, Validation | The standard library as Docly implements it |
| Directives | MASTER and INCLUDE |
| Examples | Working code: custom APIs, websites, binding, security |
| The "API" folder | How #/API/*.js endpoints work and route |
| Services | #/Services.json, scheduled jobs and events |
| The "Root" folder, The "Folder" folder | #/Root/ assets and #/Folder/ listing templates |
| site.json, Security headers | Site config, image size policy, CSP |
| Using Git with Docly | The supported git workflow — clone and push, no git init on the drive |
| About schemas, The Docly file format | What a schema is; the .docly JSON envelope |
| Create schemas | Document and folder schemas |
| Templates, Display templates | How rendering works; binding a schema to a .hash |
| Editing documents programmatically | Required before creating any .docly from outside the UI |
| Packages | Reusable components shared across workspaces |
3. State the few things that are not written down anywhere
Which workspace this is, and how it is reached. Nothing in the documentation can tell an agent this, because it differs per copy:
## Environment
| **Environment** | PRODUCTION — live and public |
| **Live URL** | https://example.com |
| **Access mode** | WebDAV mapped drive (L:\Example) |
| **Staging copy** | Example (test) — iterate there first |
| **Promote by** | Copying reviewed files from the test copy | A common Docly setup keeps several working copies of one application, with identical platform rules and completely different consequences for a careless write. If the workspace is a disposable test copy, say so — an agent that knows it will iterate faster and stop asking for confirmation it does not need.
Skills, for procedures
A skill is a folder under .claude/skills/ with a SKILL.md whose front matter describes when to use it. Unlike an import, the body stays out of context until the description matches the task — so this is where multi-step procedures belong.
---
name: docly-new-document
description: Create a new .docly document from outside the Docly UI. Use
whenever you need to add a new .docly file rather than edit an existing
one. Covers the temp+copy write pattern, the JSON envelope, schema field
mapping, and recovery from a burned filename.
--- Worth having in a Docly workspace: docly-new-document, docly-images, and docly-verify.
Do not put them in the workspace's .claude/skills/ if you work over the mapped drive. Claude Code does not discover project skills there, and restarting does not help — skill discovery depends on directory watching, which fails on a WebDAV mount. Keep them in the personal ~/.claude/skills/ folder instead, written workspace-agnostic, with the workspace-specific facts in CLAUDE.md. The instruction files themselves are unaffected. See Agent skills do not load from the mapped drive.
When the documentation is the thing that is wrong
If you work something out that is not written down, it does not belong in your CLAUDE.md. Platform behaviour goes in a KB entry; a gap or error in a page goes in the page. An instruction file is the wrong place to accumulate knowledge — it is private to one workspace, invisible to everyone else, and nobody reviews it.
Permissions
A small allowlist in .claude/settings.local.json removes most approval prompts. Keep it stingy on writes: a broad Bash(node *) entry on a production workspace defeats the rule it most needs to enforce, since forcing an edit through with shell-escaped node -e is precisely what has taken a live Docly site down before.