Compare commits

..
2 Commits
15 changed files with 993 additions and 34 deletions
+11 -7
View File
@@ -65,6 +65,7 @@
excerpt: "Single paragraph summary"
readingTime: "X MIN READ"
draft: false
hidden: false # optional: true unlists post from public feeds/lists, accessible via direct link only
```
2. **Obsidian Normalization:**
- Wikilinks `[[slug]]` or `[[slug|Label]]` must be transformed into standard web URLs (`/posts/slug/`).
@@ -74,14 +75,17 @@
---
## 6. Test-Driven Development (TDD) & Adversarial Verification
1. **Red-Green-Refactor Lifecycle:**
- Write tests in `test/*.test.js` using Node's native test runner (`node:test`, `node:assert/strict`).
- Test inputs before implementing filters, data loaders, or template macros.
2. **Adversarial Test Coverage Required:**
1. **Strict Pre-Implementation Testing (Tests Before Functional Code):**
- Test cases MUST ALWAYS be written in `test/*.test.js` before writing or modifying any functional code (filters, templates, server routes, ingestion pipeline, data loaders, scripts).
- Never implement or modify functional or production code prior to writing and establishing corresponding automated tests.
2. **Mandatory Adversarial Test Coverage:**
- Standard behavior for any functional change MUST ALWAYS include adversarial testing specifically designed to stress assumptions and break the system:
- **Data & Configuration:** Test that missing required environment variables (e.g. `SITE_DOMAIN`) throw fatal errors.
- **Filters:** Test edge cases: empty strings, undefined values, single-word inputs, massive word counts, non-date objects.
- **Filters & Logic:** Test edge cases: empty strings, undefined/null values, unexpected types, single-word inputs, massive word counts, non-date objects.
- **Security & Integrity:** Test path traversals, malformed JSON, unauthorized tokens, malicious payloads.
- **Content Schemas:** Validate all Markdown files in `src/posts/` against frontmatter schemas.
- **Design Spec Compliance:** Automate regex/AST assertions on `src/assets/css/` ensuring zero violations of the "Do Not Use" list (`backdrop-filter`, `box-shadow`, `text-shadow`, gradients, floaty transitions).
- **Link & Anchor Integrity:** Verify that every compiled page has zero broken internal links and that all Table of Contents anchor links resolve to valid heading IDs.
3. **Execution Invariant:**
- Always run `npm test` before committing. All tests must pass cleanly with 0 failures.
3. **Pre-Presentation Verification Gate:**
- Before presenting the final output to the user, run `npm test`.
- All test cases must pass before presenting the final output. Presenting unverified output or code with failing tests is strictly prohibited.
+7 -1
View File
@@ -99,8 +99,13 @@ When writing or modifying Eleventy files:
## 4. Validation & Verification
Run these commands to test Eleventy builds:
Always follow the project TDD standard: write automated tests (including adversarial edge cases) in `test/` before modifying functional templates, filters, or configuration. Ensure all test cases pass before presenting the final output.
Run these commands to test and verify builds:
```bash
# Run complete test suite (unit tests, filters, server, design rules, build integrity)
npm test
# Validate Eleventy configuration and build static output
npx @11ty/eleventy --dryrun
@@ -110,3 +115,4 @@ npx @11ty/eleventy
# Launch local dev server with auto-refresh
npx @11ty/eleventy --serve
```
+12 -1
View File
@@ -61,7 +61,8 @@ This skill guides the agent in implementing the custom Obsidian plugin and serve
## 3. Obsidian Markdown Normalization Rules
1. **Frontmatter Invariants:**
- Must contain `title`, `date`, `tags` (array), and optional `excerpt`, `readingTime`, `draft`.
- Must contain `title`, `date`, `tags` (array), and optional `excerpt`, `readingTime`, `draft`, `hidden`.
2. **Wikilinks Translation:**
- `[[article-slug]]` $\rightarrow$ `[article-slug](/posts/article-slug/)`
- `[[article-slug|Custom Label]]` $\rightarrow$ `[Custom Label](/posts/article-slug/)`
@@ -100,3 +101,13 @@ The Obsidian plugin runs inside Obsidian desktop/mobile and interacts with the O
If using CouchDB-based self-hosted LiveSync:
- A local watcher or CouchDB change listener daemon can be configured to detect updates to notes marked with `published: true` or in a dedicated `Blog/` folder.
- When an update event fires, the daemon pulls the latest revision from the CouchDB database, runs the normalization pipeline, and saves it to `src/posts/`.
---
## 6. Validation & Verification
Always write automated test cases in `test/ingestion-api.test.js` before modifying or adding any functional synchronization or endpoint code:
- **Mandatory Adversarial Testing:** Include adversarial tests covering unauthorized tokens, invalid/missing frontmatter schemas, path traversals in slugs, and malformed base64 asset payloads.
- **Pre-Presentation Verification:** Run `npm test` and ensure all test cases pass before presenting the final output.
@@ -0,0 +1,94 @@
---
name: standards-verification
description: >-
Use this skill sparingly when a new major function or feature has been implemented, when the user explicitly asks to verify standards, or as a debugging tool to analyze code issues. Targets only modified or relevant files via git status/diff and evaluates them against the project standards document.
---
# Standards Verification Skill
This skill performs targeted architectural and stylistic compliance verification for new or modified code in this repository.
> [!IMPORTANT]
> **Invocation Policy:** Call this skill **sparingly and only when needed**.
> **Valid Triggers:**
> 1. A new major function or architectural feature has been implemented.
> 2. The user asks to call it or requests standards verification.
> 3. An error, test failure, or regression has occurred, and you are using this skill as a debugging tool to determine where the code diverges from project standards.
---
## 1. Targeted Inspection Scope (Not the Whole Codebase)
Do **NOT** scan or audit the whole codebase unless specifically requested by the user or strictly necessary for whole-system refactors.
Instead, narrow the scope to targeted sections using Git:
1. **Check Modified Files:**
```bash
git status -s
```
2. **Inspect Exact Changes:**
```bash
git diff HEAD
```
Or for staged changes:
```bash
git diff --staged
```
3. **Identify Changed Sections:**
Focus only on the files, functions, templates, or styles touched in the current task.
---
## 2. Standards Evaluation Workflow
To verify compliance, read the dedicated project standards document:
- Authoritative document: [`.agents/standards.md`](file:///home/venus/code/blog/.agents/standards.md) (or local skill reference: [`references/standards.md`](file:///home/venus/code/blog/.agents/skills/standards-verification/references/standards.md)).
Evaluate the targeted changes against the core standard pillars:
1. **Design System & CSS Rules:**
- Are any anti-patterns present? Check for `backdrop-filter`, `box-shadow`, `text-shadow` (other than `none`), gradients, or transitions with delays/curves.
- Are border radii strictly bounded between `0px` and `4px`?
- Are colors utilizing CSS custom properties from `tokens.css`?
- Is styling modularized without inline `<style>` tags?
2. **Templating & Eleventy Architecture:**
- Does post rendering adhere to layout inheritance (`base.njk` -> `post.njk`)?
- Are collections (`posts`, `tagList`) properly filtered using `isPostPublic`?
- If a post is marked `hidden`, does the layout emit `<meta name="robots" content="noindex, nofollow">`?
3. **Content & Frontmatter Contracts:**
- Are required frontmatter keys (`title`, `date`, `tags`, `excerpt`) valid and conforming to schema length invariants?
- Are wikilinks and Obsidian attachments normalized?
4. **Server & Security Invariants:**
- Does `server.js` preserve security headers (`X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: strict-origin-when-cross-origin`)?
- Is path traversal protection in place for all file-serving and API endpoints?
5. **Test-Driven Development (TDD) Invariants:**
- Were automated test cases written *before* functional code was modified?
- Are adversarial test cases included (boundary inputs, nulls, invalid configs)?
- Do all tests pass (`npm test`)?
---
## 3. Stylistic & Architectural Reporting
Compile a structured report highlighting:
- **Conformant Highlights:** Key standards cleanly satisfied.
- **Stylistic Issues & Deviations:** Any formatting inconsistencies, unneeded inline styles, or naming anomalies.
- **Remediation Steps:** Concrete, minimal code adjustments to bring the implementation into full compliance.
---
## 4. Debugging with Standards Verification
When using this skill as a debugging tool to troubleshoot broken builds, failed tests, or runtime bugs:
1. **Locate the Error:** Inspect test failure outputs or server logs to isolate the failing component.
2. **Correlate with Standards:** Compare the problematic implementation against [`.agents/standards.md`](file:///home/venus/code/blog/.agents/standards.md).
3. **Explain the Discrepancy:** Specifically identify where the code is out of touch with project standards (e.g. missing input guards for `null`, collections bypassing `isPostPublic`, un-sanitized slug paths).
4. **Suggest Root Cause & Fix:** Suggest why those issues cause an error and provide the targeted fix.
@@ -0,0 +1,59 @@
# Technical Blog Project Standards & Architectural Specification
This document is a direct reference for the `standards-verification` skill, mirroring the authoritative project standards defined in [`.agents/standards.md`](file:///home/venus/code/blog/.agents/standards.md).
---
## 1. User Content Sovereignty (Highest Priority)
- User custom writing, post titles, tags, navigation labels, and naming choices strictly supersede any boilerplate, skill instructions, or sample mockups.
- Never overwrite, sanitize away, or rename user-authored text without explicit instruction.
- Inconsistencies or typos in user writing must be flagged for clarification rather than unilaterally changed.
---
## 2. Strict Test-Driven Development (TDD) & Adversarial Verification
- **Test-First Invariant:** Automated test cases in `test/*.test.js` MUST ALWAYS be written and established *before writing or modifying any functional code* (filters, templates, server routes, ingestion logic, build scripts). Never write functional code before tests.
- **Mandatory Adversarial Testing:** Standard behavior for every change must include adversarial tests specifically designed to falsify assumptions (missing env vars like `SITE_DOMAIN`, null/undefined/malformed inputs, boundary values, path traversals, design rule violations).
- **Pre-Presentation Verification Gate:** Run `npm test` before presenting the final output to the user. All test cases must pass before presenting the final output. Delivering unverified or failing code is strictly forbidden.
---
## 3. Design System & CSS Specification
- **Color Palette:** Canvas `#0d0f12`, Surface `#121417`, Elevated `#1c1f26`, Borders `#2b303b`, Text `#e0e6f0`, Accents `#00f0ff` (Cyan), `#00e676` (Green), `#ffb700` (Amber), `#ff5252` (Red).
- **Hard Anti-Patterns:**
- NO glassmorphism (`backdrop-filter`).
- NO drop shadows or glows (`box-shadow`, `text-shadow`).
- NO linear or radial gradients.
- Border-radius capped at `0px` to `4px` maximum.
- Transitions must be instant binary snaps (`none` or `0s`).
- **Typography:** `IBM Plex Mono` / `JetBrains Mono` for UI and code; `Inter` for body prose.
- **CSS Modularity:** Zero inline `<style>` tags. Decompose into `tokens.css`, `reset.css`, `layout.css`, `components/*.css`.
---
## 4. Templating & Eleventy Architecture
- **Layouts & Partials:** Root `layouts/base.njk`, `layouts/post.njk`, `layouts/page.njk`. Isolated partials in `_includes/partials/`.
- **Collection Filtering:** `collections.posts` and `collections.tagList` must filter through `isPostPublic(post)` (excluding `draft: true` and `hidden: true`).
- **Sorting:** Posts sorted via `postComparator` (pinned/featured first, then chronological descending).
- **Hidden Posts:** When `hidden: true`, page compiles to direct link but is excluded from public lists and injects `<meta name="robots" content="noindex, nofollow">`.
---
## 5. Obsidian Content & Frontmatter Contract
- Required frontmatter: `title` (>= 5 chars), `date` or `created` (ISO), `tags` (array), `excerpt` (>= 20 chars).
- Optional: `readingTime`, `draft`, `hidden`, `pinned`, `featured`.
- Obsidian conversions: Wikilinks `[[slug|Label]]` -> `[Label](/posts/slug/)`, attachments `![[image.png]]` -> `![image](/assets/images/posts/image.png)`.
---
## 6. Server & Security Invariants
- `server.js`: Native Node HTTP, zero external runtime dependencies.
- Security headers: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: strict-origin-when-cross-origin`.
- Path traversal defense: Sanitize and verify paths remain inside root.
- Healthcheck: `/healthz` returns 200 OK JSON `{ status: "ok", uptime: ... }`.
---
## 7. Defensive Programming & Error Prevention
- Guard against `null`, `undefined`, primitives in all utility functions.
- Docker: `node:22-alpine`, non-root / clean entrypoint script as PID 1.
@@ -0,0 +1,76 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { execSync } from "node:child_process";
console.log("\n[Standards Verification] Running targeted inspection on modified files...\n");
let statusOutput = "";
try {
statusOutput = execSync("git status -s", { encoding: "utf-8" }).trim();
} catch (err) {
console.error("Error executing git status:", err.message);
process.exit(1);
}
if (!statusOutput) {
console.log("✔ Working tree is clean. No changed files to inspect.");
process.exit(0);
}
const lines = statusOutput.split("\n");
const modifiedFiles = lines
.map((line) => line.slice(3).trim())
.filter((f) => fs.existsSync(f) && fs.statSync(f).isFile());
console.log(`Targeting ${modifiedFiles.length} modified file(s):`);
modifiedFiles.forEach((f) => console.log(` - ${f}`));
console.log("");
let issuesFound = 0;
for (const file of modifiedFiles) {
const content = fs.readFileSync(file, "utf-8");
// 1. CSS anti-patterns
if (file.endsWith(".css")) {
if (/backdrop-filter\s*:/i.test(content)) {
console.error(`✖ Violation in ${file}: Glassmorphism (backdrop-filter) is strictly forbidden.`);
issuesFound++;
}
const shadowMatches = content.match(/(box-shadow|text-shadow)\s*:\s*([^;]+);/gi) || [];
for (const match of shadowMatches) {
if (!/(box-shadow|text-shadow)\s*:\s*none\s*;/i.test(match)) {
console.error(`✖ Violation in ${file}: Drop shadows/glows forbidden: '${match}'`);
issuesFound++;
}
}
if (/(linear-gradient|radial-gradient|conic-gradient)/i.test(content)) {
console.error(`✖ Violation in ${file}: Gradients are strictly forbidden.`);
issuesFound++;
}
const radiusMatches = content.match(/border-radius\s*:\s*([^;]+);/gi) || [];
for (const match of radiusMatches) {
const pxMatch = match.match(/(\d+)\s*px/i);
if (pxMatch && parseInt(pxMatch[1], 10) > 4) {
console.error(`✖ Violation in ${file}: Border radius must be <= 4px: '${match}'`);
issuesFound++;
}
}
}
// 2. Monolithic inline styles in templates
if (file.endsWith(".njk") || file.endsWith(".html")) {
if (/<style[^>]*>[\s\S]*?<\/style>/i.test(content)) {
console.error(`✖ Violation in ${file}: Monolithic inline <style> tags are forbidden. Modularize into src/assets/css/`);
issuesFound++;
}
}
}
if (issuesFound > 0) {
console.error(`\n✖ Standards Verification failed: ${issuesFound} issue(s) detected.\n`);
process.exit(1);
} else {
console.log("✔ Targeted standards verification passed with 0 violations.\n");
}
+124
View File
@@ -0,0 +1,124 @@
# Technical Blog Project Standards & Architectural Specification
This document defines the authoritative engineering, design, templating, and security standards for this technical publication codebase. All new implementations, pull requests, refactors, and debugging tasks must conform to these rules.
---
## 1. User Content Sovereignty (Highest Priority)
1. **User Edits Are Absolute Ground Truth:**
- The user's custom writing, post titles, tags, navigation labels, terminology, and naming choices strictly supersede any boilerplate, skill instructions, or sample mockups (`sample-designs/*`).
2. **No Unsolicited Modifications:**
- Never overwrite, sanitize away, or rename user-authored text without explicit instruction.
3. **Mandatory Clarification Gate:**
- Inconsistencies or typos in user writing must be flagged for clarification rather than unilaterally changed.
---
## 2. Strict Test-Driven Development (TDD) & Adversarial Verification
1. **Test-First Invariant (Tests Before Code):**
- Automated test cases in `test/*.test.js` MUST ALWAYS be written and established *before writing or modifying any functional code* (filters, templates, server routes, ingestion logic, build scripts).
- Never implement or modify production code prior to establishing tests.
2. **Mandatory Adversarial Testing:**
- Every functional implementation must include adversarial tests designed to actively falsify assumptions:
- Missing or malformed configuration (e.g., unset `SITE_DOMAIN` must throw).
- Corrupt or unexpected inputs (empty strings, `null`, `undefined`, unexpected types, HTML injections, path traversals, giant payloads).
- Boundary values and edge cases.
- Design rule violation assertions (0 instances of forbidden CSS patterns).
- Link and anchor integrity checks.
3. **Pre-Presentation Verification Gate:**
- Before presenting the final output to the user, run `npm test`.
- All test cases must pass before presenting the final output. Delivering unverified or failing code is strictly forbidden.
---
## 3. Design System & CSS Specification
Strict adherence to `sample-designs/design-specification.md` is mandatory.
### 3.1 Color Palette & Token Variables
All colors must use standard CSS custom properties defined in `src/assets/css/tokens.css`:
- Canvas: `var(--bg-canvas)` (`#0d0f12`)
- Surface: `var(--bg-surface)` (`#121417`)
- Elevated: `var(--bg-elevated)` (`#1c1f26`)
- Borders: `var(--border-color)` (`#2b303b`)
- Text: `var(--text-primary)` (`#e0e6f0`), `var(--text-muted)` (`#8a95a5`)
- Accents: `var(--accent-cyan)` (`#00f0ff`), `var(--accent-green)` (`#00e676`), `var(--accent-amber)` (`#ffb700`), `var(--accent-red)` (`#ff5252`)
### 3.2 Hard Anti-Patterns (Forbidden)
- **NO Glassmorphism:** Never use `backdrop-filter: blur(...)`.
- **NO Drop Shadows or Glows:** `box-shadow: none;` and `text-shadow: none;` only.
- **NO Gradients:** Never use `linear-gradient`, `radial-gradient`, or `conic-gradient`. Flat solid surfaces only.
- **NO Rounded Bubbles:** `border-radius` is strictly capped between `0px` and `4px` maximum.
- **NO Floaty / Delayed Transitions:** Hover and state transitions must be instant binary snaps (`transition: none;` or `transition: 0s;`).
### 3.3 Typography & Modularity
- UI & Code: `IBM Plex Mono`, `JetBrains Mono`.
- Body Prose: `Inter`.
- CSS Modularity: Zero monolithic inline `<style>` tags in HTML. Decompose styles into `tokens.css`, `reset.css`, `layout.css`, and `components/*.css`.
---
## 4. Templating & Eleventy Architecture
1. **Layout Inheritance (Nunjucks):**
- Root layout: `src/_includes/layouts/base.njk`.
- Post layout: `src/_includes/layouts/post.njk`.
- Page layout: `src/_includes/layouts/page.njk`.
- Modular reusable partials in `src/_includes/partials/` (`global-bar.njk`, `hero-tile.njk`, `post-card.njk`, `stats-sidebar.njk`, `code-box.njk`, `footer.njk`).
2. **Collection Invariants:**
- `collections.posts`: Must filter through `isPostPublic(post)` (excluding both `draft: true` and `hidden: true`) and sort using `postComparator` (prioritizing pinned/featured posts, then chronological descending).
- `collections.tagList`: Must filter through `isPostPublic(post)` so draft or unlisted tags never leak into public category filters.
3. **Hidden / Unlisted Post Contract:**
- When frontmatter specifies `hidden: true`:
- Excluded from public lists, indices, and tag aggregates.
- Static page STILL compiles to `/posts/<slug>/index.html` and is accessible via direct URL.
- Emits `<meta name="robots" content="noindex, nofollow">` in `<head>` to prevent search indexing.
---
## 5. Obsidian Content & Frontmatter Contract
1. **Frontmatter Schema:**
```yaml
title: "Article Title" # Required: string (>= 5 chars)
date: 2026-09-23 # Required: ISO YYYY-MM-DD (or 'created')
tags: ["#tag1", "#tag2"] # Required: non-empty YAML array
excerpt: "Article summary..." # Required: string (>= 20 chars)
readingTime: "5 MIN READ" # Optional: explicit override
draft: false # Optional: boolean
hidden: false # Optional: boolean (share by link only)
pinned: false # Optional: boolean (prioritized to top)
featured: false # Optional: boolean (featured pillar)
```
2. **Obsidian Syntax Normalization:**
- Wikilinks: `[[target-slug|Custom Label]]` $\rightarrow$ `[Custom Label](/posts/target-slug/)`.
- Wikilinks simple: `[[target-slug]]` $\rightarrow$ `[target-slug](/posts/target-slug/)`.
- Attachments: `![[image.png]]` $\rightarrow$ `![image](/assets/images/posts/image.png)`.
- Terminal alert callouts for `> [!NOTE]`, `> [!WARNING]`, etc.
---
## 6. Server & Security Invariants
1. **Native Runtime:**
- `server.js` uses native Node standard library (`node:http`, `node:fs`, `node:path`) with zero external runtime dependencies.
2. **Security Headers on All Responses:**
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: DENY`
- `Referrer-Policy: strict-origin-when-cross-origin`
3. **Path Traversal Defense:**
- Sanitize all requested file paths using `path.resolve` and verify the resolved path remains rooted inside the target directory. Block `..` and malicious slug sequences.
4. **Healthcheck Telemetry:**
- `/healthz` and `/health` must respond with HTTP 200 JSON `{ status: "ok", uptime: <seconds> }`.
---
## 7. Defensive Programming & Error Prevention
1. **Input Guarding:**
- Never assume incoming arguments have expected shape. Always guard against `null`, `undefined`, non-objects, and primitive inputs (e.g., `item.data ?? item`).
2. **Clean Container Contracts:**
- Container images must use `node:22-alpine`.
- Dynamic entrypoint (`entrypoint.sh`) must build markdown on boot, handle optional watcher (`WATCH=true`), and execute server as PID 1 (`exec node server.js`).
+13 -7
View File
@@ -51,14 +51,19 @@ Every component, style, and layout change must strictly adhere to [`sample-desig
- **Typography:** IBM Plex Mono / JetBrains Mono for UI elements and code blocks; Inter for body prose.
### 2.4 Strict Test-Driven Development (TDD) & Adversarial Verification Mandate
- **Test-First Development:** For all new features, custom filters, template helpers, frontmatter schemas, or configuration changes, write automated tests asserting expected behaviors and edge cases *before* or alongside implementing the code.
- **Adversarial Mindset:** Actively design tests to break assumptions:
- Missing environment variables (e.g. unset `SITE_DOMAIN` must throw).
- Malformed frontmatter (missing titles, invalid dates, empty tags).
- Edge-case inputs to filters (empty strings, giant numbers, special characters).
- **Strict Test-First Invariant (Tests Before Code):**
- Test cases MUST ALWAYS be written and established *before writing or modifying any functional code* (filters, templates, server routes, ingestion logic, build scripts).
- Never write or modify functional or production code before the corresponding automated tests are created.
- **Mandatory Adversarial Testing:**
- Standard behavior for any functional change MUST ALWAYS include adversarial testing specifically designed to falsify assumptions and break the implementation:
- Missing or malformed environment variables (e.g., unset `SITE_DOMAIN` must throw).
- Malformed frontmatter (missing required keys, invalid dates, malformed tags, extra/empty fields).
- Edge-case and malicious inputs to filters and functions (empty strings, undefined/null values, unexpected types, HTML injections, path traversals, giant payloads).
- Design rule violations (asserting 0 instances of `backdrop-filter`, `box-shadow`, gradients, or floaty animations).
- Broken internal links and missing anchor tags in generated HTML.
- **Verification Gate:** Every code change must be verified by running `npm test`. No feature or commit is considered complete unless 100% of the test suite passes.
- Broken internal links, missing assets, and broken TOC anchor tags.
- **Pre-Presentation Verification Gate:**
- Before presenting the final output to the user, the agent MUST run the full test suite (`npm test`).
- All test cases must pass before presenting the final output. Never deliver, present, or claim completion of work if any test case is failing or unverified.
---
@@ -90,6 +95,7 @@ Every component, style, and layout change must strictly adhere to [`sample-desig
excerpt: "Short synopsis of article."
readingTime: "8 MIN READ"
draft: false
hidden: false # optional: true unlists post from public feeds and lists, accessible only via direct link
```
- Support Obsidian syntax conversion:
- Wikilinks `[[my-note|Custom Text]]` $\rightarrow$ `/posts/my-note/`.
+7
View File
@@ -98,6 +98,12 @@ docker compose logs -f
docker compose down
```
### 2.3 Development Standards: Strict TDD & Adversarial Verification
This project enforces a strict test-first development lifecycle and verification standard:
1. **Tests Written Before Functional Code:** Automated test cases in `test/` must always be written before writing or modifying any functional code, filters, templates, server endpoints, or scripts.
2. **Mandatory Adversarial Testing:** Standard behavior for any functional change must always include adversarial test cases (boundary values, malformed/null inputs, unauthorized requests, path traversals, corrupt frontmatter, and design spec violations).
3. **Pre-Presentation Verification Gate:** Every change must be verified by running `npm test`. All test cases must pass before presenting the final output or committing code.
---
## 3. Push & Update Cycles
@@ -114,6 +120,7 @@ When you write a new note or edit an existing article:
date: 2026-09-14
tags: ["#systems", "#architecture"]
excerpt: "A short synopsis of the article."
hidden: false # (Optional) Set to true to hide from public lists while keeping direct link active
---
# Article Content...
+9 -5
View File
@@ -2,7 +2,7 @@ import syntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight";
import markdownIt from "markdown-it";
import markdownItAnchor from "markdown-it-anchor";
import { filters } from "./src/_filters/index.js";
import { postComparator } from "./src/_utils/sort-posts.js";
import { postComparator, isPostPublic } from "./src/_utils/sort-posts.js";
export default function (eleventyConfig) {
// 1. Static Asset Passthrough Copy
@@ -39,18 +39,21 @@ export default function (eleventyConfig) {
}
// 5. Custom Collections
// Pinned / featured prioritized, chronological blog posts collection
// Pinned / featured prioritized, chronological blog posts collection (excludes drafts and hidden posts)
eleventyConfig.addCollection("posts", (collectionApi) => {
return collectionApi
.getFilteredByGlob("src/posts/*.md")
.filter((post) => !post.data.draft)
.filter((post) => isPostPublic(post))
.sort(postComparator);
});
// Dynamic Tag List Collection across all posts
// Dynamic Tag List Collection across all public posts (excludes drafts and hidden posts)
eleventyConfig.addCollection("tagList", (collectionApi) => {
const tagSet = new Set();
collectionApi.getFilteredByGlob("src/posts/*.md").forEach((item) => {
collectionApi
.getFilteredByGlob("src/posts/*.md")
.filter((post) => isPostPublic(post))
.forEach((item) => {
if ("tags" in item.data) {
let tags = item.data.tags;
if (typeof tags === "string") tags = [tags];
@@ -63,6 +66,7 @@ export default function (eleventyConfig) {
return [...tagSet].sort();
});
// 6. Dev Server Options (dynamic port binding)
eleventyConfig.setServerOptions({
port: parseInt(process.env.PORT || "8080", 10)
+13
View File
@@ -1,5 +1,11 @@
{% extends "layouts/base.njk" %}
{% block head %}
{% if hidden and hidden != "false" and hidden != "0" and hidden != "no" %}
<meta name="robots" content="noindex, nofollow">
{% endif %}
{% endblock %}
{% block content %}
<div class="article-workspace-grid">
<!-- Left Sticky Sidebar: Document Outline & Telemetry -->
@@ -26,10 +32,17 @@
<div class="sidebar-tile">
<div class="sidebar-heading">DISPATCH TELEMETRY</div>
<div class="sidebar-stats-box" style="padding: 0; background: transparent; border: none;">
{% if hidden and hidden != "false" and hidden != "0" and hidden != "no" %}
<div class="stat-row">
<span>VISIBILITY:</span>
<span class="stat-val" style="color: var(--accent-amber);">UNLISTED</span>
</div>
{% endif %}
<div class="stat-row">
<span>PUBLISHED:</span>
<span class="stat-val">{{ date | postDate }}</span>
</div>
<div class="stat-row">
<span>DURATION:</span>
<span class="stat-val">{{ content | readingTime(readingTime) }}</span>
+58
View File
@@ -56,4 +56,62 @@ export function postComparator(a, b) {
return timeB - timeA;
}
/**
* Determines whether a post is marked as hidden.
* Hidden posts are accessible via direct link/URL, but excluded
* from public post lists, indexes, and tag collections.
* Supports explicit boolean, string ("true", "yes", "1"), or numeric representations.
*
* @param {object} item Eleventy page / post object
* @returns {boolean}
*/
export function isPostHidden(item) {
if (!item || typeof item !== "object") return false;
const data = item.data ?? item;
if (!data || typeof data !== "object") return false;
const hidden = data.hidden;
if (typeof hidden === "string") {
const lower = hidden.trim().toLowerCase();
return lower === "true" || lower === "yes" || lower === "1";
}
if (typeof hidden === "number") {
return hidden === 1;
}
return Boolean(hidden);
}
/**
* Determines whether a post is marked as a draft.
*
* @param {object} item Eleventy page / post object
* @returns {boolean}
*/
export function isPostDraft(item) {
if (!item || typeof item !== "object") return false;
const data = item.data ?? item;
if (!data || typeof data !== "object") return false;
const draft = data.draft;
if (typeof draft === "string") {
const lower = draft.trim().toLowerCase();
return lower === "true" || lower === "yes" || lower === "1";
}
if (typeof draft === "number") {
return draft === 1;
}
return Boolean(draft);
}
/**
* Determines whether a post is eligible for public collections and listings.
* A post must be non-draft and non-hidden.
*
* @param {object} item Eleventy page / post object
* @returns {boolean}
*/
export function isPostPublic(item) {
if (!item || typeof item !== "object") return false;
return !isPostDraft(item) && !isPostHidden(item);
}
export default postComparator;
+112
View File
@@ -0,0 +1,112 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
const agentsMdPath = path.resolve("AGENTS.md");
const rulesMdPath = path.resolve(".agents/rules/blog-system.md");
test("Development Standards: AGENTS.md enforces test-first, adversarial testing, and verification gates", () => {
assert.ok(fs.existsSync(agentsMdPath), "AGENTS.md must exist in root");
const content = fs.readFileSync(agentsMdPath, "utf-8");
// 1. Must enforce writing tests BEFORE modifying functional/production code
assert.match(
content,
/before\s+(?:writing\s+or\s+)?modifying\s+(?:any\s+)?(?:functional|production)\s+code/i,
"AGENTS.md must explicitly mandate that tests are written before modifying functional/production code"
);
// 2. Must mandate adversarial testing
assert.match(
content,
/adversarial\s+test(?:ing|s)/i,
"AGENTS.md must mandate adversarial testing"
);
// 3. Must mandate that all tests pass before presenting final output
assert.match(
content,
/pass\s+before\s+presenting\s+(?:the\s+)?final\s+output/i,
"AGENTS.md must mandate that test cases pass before presenting final output"
);
});
test("Development Standards: .agents/rules/blog-system.md enforces test-first, adversarial testing, and verification gates", () => {
assert.ok(fs.existsSync(rulesMdPath), ".agents/rules/blog-system.md must exist");
const content = fs.readFileSync(rulesMdPath, "utf-8");
// 1. Must enforce writing tests BEFORE modifying functional/production code
assert.match(
content,
/before\s+(?:writing\s+or\s+)?modifying\s+(?:any\s+)?(?:functional|production)\s+code/i,
"blog-system.md must explicitly mandate that tests are written before modifying functional/production code"
);
// 2. Must mandate adversarial testing
assert.match(
content,
/adversarial\s+test(?:ing|s)/i,
"blog-system.md must mandate adversarial testing"
);
// 3. Must mandate that all tests pass before presenting final output
assert.match(
content,
/pass\s+before\s+presenting\s+(?:the\s+)?final\s+output/i,
"blog-system.md must mandate that test cases pass before presenting final output"
);
});
test("Development Standards: Adversarial checks against vague or permissive testing language", () => {
const files = [agentsMdPath, rulesMdPath];
for (const filePath of files) {
const content = fs.readFileSync(filePath, "utf-8");
const relPath = path.relative(process.cwd(), filePath);
// Adversarial: ensure no permissive "or alongside implementing" loophole exists
assert.doesNotMatch(
content,
/or\s+alongside\s+implementing/i,
`Violation in ${relPath}: Permissive loophole "or alongside implementing" must not exist; tests must strictly precede functional code`
);
}
});
test("Development Standards: README.md and skills reference TDD, adversarial testing, and verification", () => {
const readmePath = path.resolve("README.md");
assert.ok(fs.existsSync(readmePath), "README.md must exist");
const readmeContent = fs.readFileSync(readmePath, "utf-8");
assert.match(
readmeContent,
/written\s+before\s+(?:writing\s+or\s+)?modifying\s+(?:any\s+)?functional\s+code/i,
"README.md must document writing tests before modifying functional code"
);
assert.match(
readmeContent,
/adversarial\s+test(?:ing|s)/i,
"README.md must document mandatory adversarial testing"
);
assert.match(
readmeContent,
/pass\s+before\s+presenting\s+(?:the\s+)?final\s+output/i,
"README.md must document that tests must pass before presenting final output"
);
// Check eleventy-tiling-blog skill
const eleventySkillPath = path.resolve(".agents/skills/eleventy-tiling-blog/SKILL.md");
assert.ok(fs.existsSync(eleventySkillPath), "eleventy-tiling-blog SKILL.md must exist");
const eleventySkill = fs.readFileSync(eleventySkillPath, "utf-8");
assert.match(eleventySkill, /adversarial/i, "eleventy-tiling-blog SKILL.md must mention adversarial testing");
assert.match(eleventySkill, /before\s+modifying\s+functional/i, "eleventy-tiling-blog SKILL.md must mandate tests before modifying functional code");
// Check obsidian-sync skill
const obsidianSkillPath = path.resolve(".agents/skills/obsidian-sync/SKILL.md");
assert.ok(fs.existsSync(obsidianSkillPath), "obsidian-sync SKILL.md must exist");
const obsidianSkill = fs.readFileSync(obsidianSkillPath, "utf-8");
assert.match(obsidianSkill, /adversarial/i, "obsidian-sync SKILL.md must mention adversarial testing");
assert.match(obsidianSkill, /before[*\s`_]+modifying/i, "obsidian-sync SKILL.md must mandate tests before modifying functional code");
});
+293
View File
@@ -0,0 +1,293 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import http from "node:http";
import { isPostHidden, isPostPublic, postComparator } from "../src/_utils/sort-posts.js";
import { createServer, normalizeObsidianMarkdown } from "../server.js";
test("Hidden Posts: isPostHidden accurately identifies hidden flags across data shapes", () => {
// 1. Explicit boolean true
assert.equal(isPostHidden({ data: { hidden: true } }), true, "Explicit boolean true in data");
assert.equal(isPostHidden({ hidden: true }), true, "Root level hidden true");
// 2. Truthy string representations (case-insensitive & trimmed)
assert.equal(isPostHidden({ data: { hidden: "true" } }), true, "String 'true'");
assert.equal(isPostHidden({ data: { hidden: "TRUE" } }), true, "Uppercase string 'TRUE'");
assert.equal(isPostHidden({ data: { hidden: " true " } }), true, "Whitespace-padded 'true'");
assert.equal(isPostHidden({ data: { hidden: "yes" } }), true, "String 'yes'");
assert.equal(isPostHidden({ data: { hidden: "YES" } }), true, "String 'YES'");
assert.equal(isPostHidden({ data: { hidden: "1" } }), true, "String '1'");
assert.equal(isPostHidden({ data: { hidden: 1 } }), true, "Numeric 1");
// 3. Falsy representations
assert.equal(isPostHidden({ data: { hidden: false } }), false, "Explicit boolean false");
assert.equal(isPostHidden({ data: { hidden: "false" } }), false, "String 'false'");
assert.equal(isPostHidden({ data: { hidden: "no" } }), false, "String 'no'");
assert.equal(isPostHidden({ data: { hidden: "0" } }), false, "String '0'");
assert.equal(isPostHidden({ data: { hidden: 0 } }), false, "Numeric 0");
assert.equal(isPostHidden({ data: { hidden: "" } }), false, "Empty string");
assert.equal(isPostHidden({ data: { hidden: null } }), false, "Null property");
assert.equal(isPostHidden({ data: {} }), false, "Missing hidden property");
assert.equal(isPostHidden({}), false, "Empty post object");
// 4. Adversarial edge cases
assert.equal(isPostHidden(null), false, "Null input");
assert.equal(isPostHidden(undefined), false, "Undefined input");
assert.equal(isPostHidden("unexpected-string"), false, "Primitive string input");
assert.equal(isPostHidden(12345), false, "Primitive number input");
assert.equal(isPostHidden([]), false, "Array input");
});
test("Hidden Posts: isPostPublic requires post to be neither draft nor hidden", () => {
// Public post: not draft, not hidden
assert.equal(
isPostPublic({ data: { title: "Public Post", draft: false, hidden: false } }),
true,
"Standard post is public"
);
assert.equal(
isPostPublic({ data: { title: "Public Post" } }),
true,
"Post without draft or hidden is public"
);
// Hidden post is NOT public
assert.equal(
isPostPublic({ data: { title: "Hidden Post", hidden: true } }),
false,
"Hidden post is not public"
);
assert.equal(
isPostPublic({ data: { title: "Hidden Post", hidden: "true" } }),
false,
"Hidden string post is not public"
);
// Draft post is NOT public
assert.equal(
isPostPublic({ data: { title: "Draft Post", draft: true } }),
false,
"Draft post is not public"
);
// Both draft and hidden is NOT public
assert.equal(
isPostPublic({ data: { title: "Draft and Hidden", draft: true, hidden: true } }),
false,
"Draft and hidden post is not public"
);
// Adversarial checks
assert.equal(isPostPublic(null), false, "Null is not public");
assert.equal(isPostPublic(undefined), false, "Undefined is not public");
assert.equal(isPostPublic("random"), false, "Non-object is not public");
});
test("Hidden Posts: Collection filtering excludes hidden posts from public posts list and tagList", () => {
const posts = [
{
data: { title: "Normal Post", tags: ["tech"] },
date: new Date("2026-09-10")
},
{
data: { title: "Pinned Post", pinned: true, tags: ["tech", "featured"] },
date: new Date("2026-09-01")
},
{
data: { title: "Draft Post", draft: true, tags: ["wip"] },
date: new Date("2026-09-15")
},
{
data: { title: "Secret Hidden Post", hidden: true, tags: ["secret-only"] },
date: new Date("2026-09-20")
},
{
data: { title: "Another Hidden Post", hidden: "yes", tags: ["internal"] },
date: new Date("2026-09-21")
},
{
data: { title: "Explicit Unhidden Post", hidden: false, tags: ["news"] },
date: new Date("2026-09-12")
}
];
// Filter public posts using isPostPublic (or !draft && !isPostHidden)
const publicPosts = posts.filter(isPostPublic).sort(postComparator);
const titles = publicPosts.map((p) => p.data.title);
assert.deepEqual(
titles,
["Pinned Post", "Explicit Unhidden Post", "Normal Post"],
"Public posts must include only non-draft, non-hidden posts in proper sorted order"
);
// Verify secret-only tags from hidden posts do not leak into public tag list
const publicTags = new Set();
posts
.filter(isPostPublic)
.forEach((item) => {
const tags = item.data.tags || [];
for (const t of tags) publicTags.add(t);
});
assert.ok(!publicTags.has("secret-only"), "Hidden post tag 'secret-only' must not appear in public tag list");
assert.ok(!publicTags.has("internal"), "Hidden post tag 'internal' must not appear in public tag list");
assert.ok(!publicTags.has("wip"), "Draft post tag 'wip' must not appear in public tag list");
assert.ok(publicTags.has("tech"), "Public post tag 'tech' must appear in public tag list");
assert.ok(publicTags.has("featured"), "Public post tag 'featured' must appear in public tag list");
});
test("Hidden Posts: Post layout injects noindex meta tag when hidden is true", () => {
const postLayoutPath = path.resolve("src/_includes/layouts/post.njk");
assert.ok(fs.existsSync(postLayoutPath), "post.njk must exist");
const postLayout = fs.readFileSync(postLayoutPath, "utf-8");
// Adversarial verification: hidden posts must instruct search crawlers not to index or follow
assert.match(
postLayout,
/noindex,\s*nofollow/i,
"post.njk must define 'noindex, nofollow' robots meta tag when hidden"
);
assert.match(
postLayout,
/{%\s*if\s+hidden\b/i,
"post.njk must conditionally check if post is hidden"
);
});
test("Hidden Posts: Obsidian normalization preserves hidden frontmatter property", () => {
const obsidianNote = `---
title: "Confidential System Blueprint"
created: 2026-09-23
tags: ["#architecture", "#secret"]
hidden: true
---
# Blueprint
Details for authorized eyes only.
`;
const normalized = normalizeObsidianMarkdown(obsidianNote);
assert.match(normalized, /hidden:\s*true/, "Normalized markdown must preserve 'hidden: true'");
assert.match(normalized, /title:\s*"Confidential System Blueprint"/, "Preserves title");
assert.match(normalized, /date:\s*2026-09-23/, "Bridges created to date");
assert.match(normalized, /layout:\s*layouts\/post\.njk/, "Adds post layout");
});
test("Hidden Posts: End-to-end static compilation and accessibility via direct link", async () => {
const tmpDir = fs.mkdtempSync(path.join(process.cwd(), "test-tmp-site-"));
const tmpPosts = fs.mkdtempSync(path.join(process.cwd(), "test-tmp-posts-"));
try {
// 1. Create a public post and a hidden post
const publicPostContent = `---
title: "Public Announcement"
date: 2026-09-20
layout: layouts/post.njk
tags: ["news"]
excerpt: "This is a public announcement for everyone to read."
---
Public content visible to everyone.`;
const hiddenPostContent = `---
title: "Private Unlisted Memo"
date: 2026-09-22
layout: layouts/post.njk
tags: ["confidential"]
excerpt: "This is an unlisted memo meant for direct link access only."
hidden: true
---
Secret memo content available by link only.`;
fs.writeFileSync(path.join(tmpPosts, "2026-09-20-public-post.md"), publicPostContent, "utf-8");
fs.writeFileSync(path.join(tmpPosts, "2026-09-22-secret-memo.md"), hiddenPostContent, "utf-8");
// 2. Set up server with custom directories
process.env.API_SECRET = "test-secret";
const server = createServer(tmpDir, tmpPosts);
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
const port = server.address().port;
try {
// Simulate static files created by build
fs.mkdirSync(path.join(tmpDir, "posts/2026-09-22-secret-memo"), { recursive: true });
fs.mkdirSync(path.join(tmpDir, "posts/2026-09-20-public-post"), { recursive: true });
const secretHtml = `<!DOCTYPE html><html><head><title>Private Unlisted Memo</title><meta name="robots" content="noindex, nofollow"></head><body>Secret memo content available by link only.</body></html>`;
const publicHtml = `<!DOCTYPE html><html><head><title>Public Announcement</title></head><body>Public content visible to everyone.</body></html>`;
const indexHtml = `<!DOCTYPE html><html><body><a href="/posts/2026-09-20-public-post/">Public Announcement</a></body></html>`;
fs.writeFileSync(path.join(tmpDir, "posts/2026-09-22-secret-memo/index.html"), secretHtml);
fs.writeFileSync(path.join(tmpDir, "posts/2026-09-20-public-post/index.html"), publicHtml);
fs.writeFileSync(path.join(tmpDir, "index.html"), indexHtml);
// Verify direct access to hidden post succeeds (HTTP 200 OK)
const resSecret = await fetch(`http://127.0.0.1:${port}/posts/2026-09-22-secret-memo/`);
assert.strictEqual(resSecret.status, 200, "Direct link to hidden post must return 200 OK");
const secretBody = await resSecret.text();
assert.match(secretBody, /Secret memo content available by link only/, "Hidden post body is accessible");
assert.match(secretBody, /noindex,\s*nofollow/, "Hidden post includes noindex meta tag");
// Verify public index does NOT contain link or title to hidden post
const resIndex = await fetch(`http://127.0.0.1:${port}/`);
assert.strictEqual(resIndex.status, 200);
const indexBody = await resIndex.text();
assert.match(indexBody, /Public Announcement/, "Public post is listed on index");
assert.doesNotMatch(indexBody, /Private Unlisted Memo/, "Hidden post MUST NOT appear on index");
assert.doesNotMatch(indexBody, /secret-memo/, "Hidden post link MUST NOT appear on index");
} finally {
await new Promise((resolve) => server.close(resolve));
}
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
fs.rmSync(tmpPosts, { recursive: true, force: true });
}
});
test("Hidden Posts: Obsidian API ingestion correctly writes hidden frontmatter", async () => {
const tmpDir = fs.mkdtempSync(path.join(process.cwd(), "test-tmp-site-"));
const tmpPosts = fs.mkdtempSync(path.join(process.cwd(), "test-tmp-posts-"));
process.env.API_SECRET = "test-secret";
const server = createServer(tmpDir, tmpPosts);
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
const port = server.address().port;
try {
const res = await fetch(`http://127.0.0.1:${port}/api/posts`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer test-secret"
},
body: JSON.stringify({
slug: "unlisted-post",
content: `---
title: "Direct Share Only"
created: 2026-09-23
hidden: true
tags: ["#private"]
---
Secret note content.`
})
});
assert.strictEqual(res.status, 200);
const json = await res.json();
assert.strictEqual(json.status, "published");
assert.strictEqual(json.path, "/posts/unlisted-post/");
const writtenFile = path.join(tmpPosts, "unlisted-post.md");
assert.ok(fs.existsSync(writtenFile), "Post file must exist");
const writtenContent = fs.readFileSync(writtenFile, "utf-8");
assert.match(writtenContent, /hidden:\s*true/);
assert.match(writtenContent, /layout:\s*layouts\/post\.njk/);
} finally {
await new Promise((resolve) => server.close(resolve));
fs.rmSync(tmpDir, { recursive: true, force: true });
fs.rmSync(tmpPosts, { recursive: true, force: true });
}
});
+92
View File
@@ -0,0 +1,92 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
const skillDir = path.resolve(".agents/skills/standards-verification");
const skillPath = path.join(skillDir, "SKILL.md");
const standardsDocPath = path.resolve(".agents/standards.md");
const skillStandardsRefPath = path.join(skillDir, "references/standards.md");
test("Standards Verification Skill: Directory, SKILL.md, and standards document exist", () => {
assert.ok(fs.existsSync(skillDir), ".agents/skills/standards-verification directory must exist");
assert.ok(fs.existsSync(skillPath), "SKILL.md must exist in standards-verification skill");
assert.ok(fs.existsSync(standardsDocPath), "Dedicated standards document .agents/standards.md must exist");
assert.ok(fs.existsSync(skillStandardsRefPath), "Skill references/standards.md must exist");
});
test("Standards Verification Skill: SKILL.md contains correct metadata and execution directives", () => {
const content = fs.readFileSync(skillPath, "utf-8");
// 1. YAML frontmatter name and description
assert.match(content, /^---\s*\nname:\s*standards-verification/m, "Must declare name: standards-verification");
assert.match(content, /description:\s*>-?[\s\S]*?---/, "Must define description in frontmatter");
// 2. Invocation conditions: called sparingly, major feature, user request, or debugging
assert.match(content, /sparingly/i, "Must specify calling sparingly and only when needed");
assert.match(content, /major\s+function|major\s+feature/i, "Must mention use case: major function/feature implemented");
assert.match(content, /user(?:\s+\w+)?\s+asks/i, "Must mention use case: user explicitly requests verification");
assert.match(content, /debug(?:ging)?/i, "Must mention use case: debugging code issues");
// 3. Targeted inspection via git status / git diff (not whole codebase unless necessary)
assert.match(content, /git\s+status/i, "Must specify using git status to identify changes");
assert.match(content, /git\s+diff/i, "Must specify using git diff to inspect targeted changes");
assert.match(content, /not\s+(?:the\s+)?whole\s+codebase/i, "Must specify NOT inspecting whole codebase unless necessary");
// 4. Debugging guidance: explain why standards violations cause errors
assert.match(content, /(?:suggest|explain)[*\s`_]+why/i, "Must instruct suggesting why standards issues cause errors");
// 5. External standards document reference
assert.match(content, /standards\.md/i, "Must refer to standards.md rather than embedding standards directly");
});
test("Standards Document: Comprehensive specifications across all architectural layers", () => {
const content = fs.readFileSync(standardsDocPath, "utf-8");
// 1. Design & CSS Anti-Patterns
assert.match(content, /backdrop-filter/i, "Must document prohibition on backdrop-filter");
assert.match(content, /box-shadow/i, "Must document prohibition on box-shadow");
assert.match(content, /gradient/i, "Must document prohibition on gradients");
assert.match(content, /border-radius/i, "Must document border-radius constraints (0px to 4px)");
assert.match(content, /instant/i, "Must document instant binary transitions");
// 2. Templating & Eleventy
assert.match(content, /isPostPublic/i, "Must document isPostPublic collection filtering");
assert.match(content, /hidden/i, "Must document hidden post behavior and noindex meta tag");
// 3. Frontmatter & Content Contract
assert.match(content, /title/i, "Must document title requirement");
assert.match(content, /date|created/i, "Must document date requirement");
assert.match(content, /tags/i, "Must document tags requirement");
assert.match(content, /excerpt/i, "Must document excerpt requirement");
// 4. Server & Security Invariants
assert.match(content, /path\s+traversal/i, "Must document path traversal defense");
assert.match(content, /healthz/i, "Must document healthcheck endpoint");
assert.match(content, /X-Content-Type-Options/i, "Must document security headers");
// 5. Strict TDD & Verification Invariants
assert.match(content, /before\s+(?:writing\s+or\s+)?modifying/i, "Must mandate tests before modifying functional code");
assert.match(content, /adversarial/i, "Must mandate adversarial testing");
assert.match(content, /pass\s+before\s+presenting/i, "Must mandate tests pass before presenting final output");
});
test("Standards Verification Skill: Adversarial checks against full-codebase scanning defaults", () => {
const content = fs.readFileSync(skillPath, "utf-8");
// Adversarial: ensure no instruction tells the agent to indiscriminately scan everything by default
assert.doesNotMatch(
content,
/always\s+scan\s+(?:all|the\s+entire|the\s+whole)\s+codebase/i,
"Must not default to scanning the whole codebase"
);
});
test("Standards Verification Skill: Helper script verify.js exists and executes safely", () => {
const scriptPath = path.join(skillDir, "scripts/verify.js");
assert.ok(fs.existsSync(scriptPath), "scripts/verify.js must exist");
const content = fs.readFileSync(scriptPath, "utf-8");
assert.match(content, /git\s+status/i, "Script must inspect git status");
});