thought-layer-build — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited thought-layer-build (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are not generating a prompt to paste elsewhere. You are the agent that builds. The hardened PRD is your brief; build it now, static-first, and verify it actually runs. AI made building cheap, so the value is building the right thing, honestly, and shipping something real and ownable. One pass, then verify. This is a build step, not a second framework: no panel, no stages, no per-turn loop.
For the fastest possible deployable thing, or as a floor when a full build is too much, you can run the tl_scaffold tool first (or instead) - it deterministically writes a branded, SEO-complete static landing site you can ship immediately, then build the real product on top.
Read the state file first (next section). Then:
state.prd with a non-empty markdown and a non-empty requirements array.state.grill.done === true (the PRD was actually hardened). If prd exists but the grill is absent or not done, build anyway and warn in one line: "Building from a drafted-but-not-grilled PRD - gaps the grill would have caught may surface; /tl-grill first would tighten it."prd.markdown / no requirements at all: say so and point to /tl (the full framework) or at least /tl-prd then /tl-grill. Proceed cold only if the user explicitly says to, and record in DECISIONS.md that the spec was incomplete.The spec lives in .thought-layer/state.json (or a named file). Honor the same selection as the rest of the kit: an explicit --path / tool path wins, then THOUGHT_LAYER_STATE, then the default. If the tl_state tool is available (Pi), tl_state read; otherwise tl read --json (the CLI). If neither a path nor the env is set, tl list (or tl_state list) first and ask which idea to build when several exist, then stick to that path for the whole session and write build.json next to it.
Assemble the brief from the state:
prd.markdown - the full spec (your primary source of truth).prd.glossary [{term, definition}] - the ubiquitous language to enforce.grill.requirements if grill.done, else prd.requirements [{id, category, text}] - the R-IDs to build and trace.prd.weakestAssumptions - flag these as known gaps in DECISIONS.md.brand - the identity to apply (skip if absent; see ground rules).bizModel.assumptions / key answers - a one-line business context.feedback to-dos and any "Open validation to-dos" in the PRD - known gaps the founder set aside; build around them, do not treat them as blockers.TRACEABILITY.md mapping each R-ID to the file/component that implements it and how it is verified (a test, a manual check, or "deferred").state.brand is present, apply its colors, type, voice, and name throughout. If absent, pick a clean, neutral, accessible default and record the choice in DECISIONS.md.DECISIONS.md rather than blocking.Default to a self-contained static site or a Vite/Astro/static build whose output is one publish directory (dist/); set the manifest hasBackend: false. Pure HTML/CSS/JS where the interactivity allows; a bundler only when the spec warrants it.
Escalate to a backend ONLY if a requirement genuinely needs one - apply this three-question test to the R-IDs:
If all three are no, build static, full stop. localStorage, static data files, BYOK client-side AI calls, and third-party embeddable widgets do not count as a backend - many specs that sound like they need a server can ship a compelling static slice first.
When a backend is genuinely required, build it for real (do not just warn). Build the static front end as above, set hasBackend: true and a one-line backendNote, and emit a coherent, buildable serverless backend alongside it:
netlify/functions/. Name each file in the ubiquitous language (the glossary term for what it does, e.g. netlify/functions/dispatch.ts), open it with a comment naming the R-ID it implements, and keep it inside the out-of-scope boundary. Each function reads its inputs, talks to the database, and returns JSON. Give the front end a clear seam: it calls the function at fetch('/.netlify/functions/<name>'), never a hardcoded host.create table if not exists ...).@neondatabase/serverless, added to the product's package.json, never the kit's) and read the connection string from DATABASE_URL (Netlify sets NETLIFY_DATABASE_URL when you provision managed Neon, so read DATABASE_URL and map it). Neon is the single documented default and is overridable to any Postgres by pointing DATABASE_URL elsewhere; state that in BACKEND.md and do not invent a second provider.DATABASE_URL plus any others), each as a bare NAME= under a one-line comment. Never write a real value; real values live only in the host environment.[functions] table declaring directory = "netlify/functions". Keep the static publish dir and the SPA redirect intact.tl deploy ships only the front end today), how to provision Neon, the env-var table, the function-to-R-ID table, and the manual netlify deploy steps. The kit's renderBackendGuide and renderEnvExample helpers (in core/backend.ts) produce a dash-free skeleton if you have the core available; otherwise write the same content by hand..env and .env.* but un-ignores the contract with !.env.example. Without that line the env contract is silently un-committable, and the deploy step cannot read it.Then record the backend in the manifest's backend block (shape below). Do not attempt to deploy in this step. The deploy is the next step: tl deploy (or the thought-layer-deploy skill) reads this backend block and ships the functions plus the declared env var names via the user's Netlify CLI into their own account, with DATABASE_URL bring-your-own by default. Point the user at BACKEND.md for the details and the opt-in database steps.
This is the cheap moat. Either run tl_scaffold to lay these down for you, or build them by hand into the publish dir, and set each build.json.seo.* flag from the file you actually emitted:
@graph (Organization + Person(founder) cross-referenced, WebSite, and the page type that matches the content); never a type that does not match visible content./llms.txt (title, one-paragraph summary, links to the key pages, a short FAQ).sitemap.xml, robots.txt (allow search + AI crawlers, point to the sitemap), a canonical link, Open Graph + Twitter meta, and a 1200x630 social image.netlify.toml (publish dir + SPA redirect) and a SEO.md documenting where each item lives and what to fill later (the sameAs links, the social image, the real domain).Do not declare victory - check:
npm install then the build command; confirm it exits clean (capture the command into build.json.buildCommand). For pure static: confirm the files exist.dist/index.html (or your entry) exists and is non-trivial. Where a preview or browser tool is available, load it and confirm it renders; otherwise inspect the built HTML for the expected title/nav/hero and that the mobile viewport meta is set.build.json.requirements.build.json.seo.* from reality, not intent.netlify/functions/ has one file per backend R-ID; .env.example is values-free (every variable line is a bare NAME=, never a value); schema.sql is non-empty; netlify.toml declares the functions directory; .gitignore has !.env.example. Do not try to run the backend or reach a database here (there is no DATABASE_URL in the build env); confirm the artifact is coherent and buildable, not live.If you cannot fully build it (the spec is thin, a requirement you genuinely cannot satisfy, time or tool limits), ship the best static slice that loads plus DECISIONS.md noting every gap and TRACEABILITY.md marking the unbuilt R-IDs deferred. A partial, honest, deployable artifact beats a complete-looking broken one. Never fake a green check - verified.buildRan: false is an acceptable, honest value. If even a slice is too much, run tl_scaffold to leave a real deployable landing page as the floor.
Write three files with your own file tools (the manifest is NOT a tl_state artifact - it is a plain sidecar):
.thought-layer/build.json - the deploy contract, co-located with the state file you read. Shape (fill what you can, never fake a field):{ "app": "thought-layer", "kind": "build", "version": 1, "builtAt": "<ISO>",
"producer": "agent", "publishDir": "dist", "entry": "index.html",
"stack": "static|vite|astro|next-static|other", "hasBackend": false, "backendNote": null,
"backend": null,
"buildCommand": null, "installCommand": null, "nodeVersion": "20",
"provenance": { "stateFile": "<the file you read>", "prdTs": <state.prd.ts>, "grillDone": <bool>, "fromSpeedrun": <bool> },
"requirements": { "total": 0, "built": 0, "deferred": 0, "deferredIds": [] },
"seo": { "jsonLd": true, "llmsTxt": true, "sitemap": true, "robots": true, "canonical": true, "openGraph": true, "socialImage": false, "semanticHtml": true, "seoDoc": true, "netlifyToml": true },
"artifacts": { "traceability": "TRACEABILITY.md", "decisions": "DECISIONS.md", "seo": "SEO.md" },
"verified": { "buildRan": true, "publishDirExists": true, "entryLoads": true, "notes": "..." } }publishDir + entry are load-bearing - the deploy step reads them. (The tl_scaffold tool writes this same manifest with producer: "scaffold".)
When hasBackend is true, populate backend (leave it null for a static build). This is the forward-looking contract the backend deploy automation will consume:
"backend": {
"backendKind": "serverless", "functionsDir": "netlify/functions",
"runtime": "nodejs20.x", "nodeVersion": "20",
"envVars": [{ "name": "DATABASE_URL", "required": true, "description": "Neon Postgres connection string" }],
"database": { "provider": "neon", "schemaFile": "schema.sql", "envVar": "DATABASE_URL" },
"guide": "BACKEND.md" }DECISIONS.md - every choice the spec did not pin down, one line each, with the reason.TRACEABILITY.md - the R-ID map. (SEO.md comes from the SEO step.)The build output and the three files live on disk; the portable state.json stays focused on validation and design. The only optional state touch is a best-effort cursor bump - tl_state cursor (or tl cursor) with { "backboneStage": 15, "phase": "built" } - pure provenance; if the tool is absent or it fails, the build still succeeded. Tell the user where the artifact is (<publishDir>) and that the next step is the deploy - the thought-layer-deploy skill (/tl-deploy) or the deploy tool / tl deploy CLI, which reads build.json and takes it live to a URL they own.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.