# Phase 1: Foundation

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Establish all backend data types, schemas, memory linking, prompt templates, and the knowledge package convention — everything needed before any UI or discovery features.

**Architecture:** Extend the existing Storage interface with knowledge-specific types. Knowledge packages are stored as regular MemoryRecords following the `packages/{uuid}/*` key convention. Memory links are stored as MemoryRecords at `links/{hash}/{hash}`. JSON Schemas for each content type are registered via the existing Schema Locking system. Two prompt templates (human + agent) are stored as memory records with placeholder substitution at copy-time.

**Tech Stack:** TypeScript, Express 5, existing Storage interface, existing Schema Locking API, @noble/ed25519 for any ID generation.

---

## Task 1: Knowledge Package Type Definitions

**Files:**
- Modify: `aimeat/src/storage/interface.ts` (append after existing types, before Storage interface composition)

**Step 1: Write the type definitions**

Add these types to `interface.ts` after the existing type definitions (around line 670, before the Storage interface):

```typescript
/* ── Knowledge System types ── */

export type KnowledgeContentType =
  | 'idea' | 'research' | 'plan' | 'dataset' | 'document'
  | 'tutorial' | 'collection' | 'article' | 'story' | 'fiction';

export type KnowledgeSynthesisLevel = 'original' | 'assisted' | 'synthesized' | 'ai-generated';
export type KnowledgeMaturity = 'draft' | 'review' | 'published';
export type KnowledgeLinkRelation = 'related-to' | 'extends' | 'derived-from' | 'contradicts' | 'supersedes' | 'references';

export interface KnowledgeReference {
  url: string;
  title: string;
  accessed: string;           // ISO 8601
  verified: boolean;
  note?: string;
}

export interface KnowledgeEntryDescriptor {
  key: string;
  title: string;
  visibility: 'private' | 'owner' | 'public';
  schema?: string;
}

export interface KnowledgeLink {
  target: string;
  relation: KnowledgeLinkRelation;
  description: string;
  linked_at: string;          // ISO 8601
}

export interface KnowledgeSynthesis {
  level: KnowledgeSynthesisLevel;
  description: string;
  model?: string;
}

export interface KnowledgeSharing {
  catalog_listed: boolean;
  allow_clone: boolean;
  license?: string;
  morsel_price: number;       // 0 = free
}

export interface KnowledgeManifest {
  type: 'knowledge-package';
  name: string;
  version: string;
  author: string;             // GHII of the package creator
  created: string;            // ISO 8601
  updated: string;            // ISO 8601
  content_type: KnowledgeContentType;
  tags: string[];
  language: string;           // ISO 639-1
  maturity: KnowledgeMaturity;
  synthesis: KnowledgeSynthesis;
  references: KnowledgeReference[];
  entries: KnowledgeEntryDescriptor[];
  links: KnowledgeLink[];
  sharing: KnowledgeSharing;
}

export interface MemoryLinkRecord {
  source: string;             // Source memory key
  target: string;             // Target memory key
  relation: KnowledgeLinkRelation;
  description: string;
  linked_at: string;          // ISO 8601
  linked_by: string;          // GHII of who created the link
}

export type OperatorReviewReason =
  | 'routine_review' | 'legal_compliance' | 'community_report'
  | 'content_quality' | 'storage_issue' | 'custom';

export type OperatorReviewAction = 'approve' | 'flag' | 'delist' | 'restrict' | 'note';

export interface OperatorReviewRecord {
  id: string;                 // UUID
  packageId: string;          // The packages/{uuid}/manifest key
  operatorGaii: string;       // Operator who reviewed
  reason: OperatorReviewReason;
  customText?: string;        // For 'custom' reason
  action: OperatorReviewAction;
  timestamp: string;          // ISO 8601
}
```

**Step 2: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS (no errors — we only added new types, nothing references them yet)

**Step 3: Commit**

```bash
git add aimeat/src/storage/interface.ts
git commit -m "feat(knowledge): add Knowledge System type definitions to storage interface"
```

---

## Task 2: Knowledge Repository Interface

**Files:**
- Create: `aimeat/src/storage/repositories/knowledge.repository.ts`
- Modify: `aimeat/src/storage/repositories/index.ts` (add export)
- Modify: `aimeat/src/storage/interface.ts` (extend Storage interface)

**Step 1: Create the repository interface**

Create `aimeat/src/storage/repositories/knowledge.repository.ts`:

```typescript
import type { MemoryLinkRecord, OperatorReviewRecord } from '../interface.js';

export interface KnowledgeRepository {
  /* ── Memory Links ── */
  createLink(record: MemoryLinkRecord): Promise<MemoryLinkRecord>;
  getLink(source: string, target: string): Promise<MemoryLinkRecord | null>;
  listLinks(key: string, opts?: { direction?: 'outgoing' | 'incoming' | 'both'; relation?: string }): Promise<MemoryLinkRecord[]>;
  deleteLink(source: string, target: string): Promise<boolean>;
  findBrokenLinks(ownerGaii: string): Promise<MemoryLinkRecord[]>;

  /* ── Operator Reviews ── */
  createReview(record: OperatorReviewRecord): Promise<OperatorReviewRecord>;
  listReviews(packageId: string): Promise<OperatorReviewRecord[]>;
  listAllReviews(opts?: { page?: number; perPage?: number }): Promise<OperatorReviewRecord[]>;
}
```

**Step 2: Export from index**

Add to `aimeat/src/storage/repositories/index.ts`:

```typescript
export type { KnowledgeRepository } from './knowledge.repository.js';
```

**Step 3: Extend Storage interface**

In `aimeat/src/storage/interface.ts`, add `KnowledgeRepository` to the Storage interface composition (around line 852). Find the line that reads `export interface Storage extends` and add `KnowledgeRepository` to the list.

**Step 4: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: FAIL — Memory implementation doesn't implement KnowledgeRepository yet. This is expected.

**Step 5: Commit**

```bash
git add aimeat/src/storage/repositories/knowledge.repository.ts aimeat/src/storage/repositories/index.ts aimeat/src/storage/interface.ts
git commit -m "feat(knowledge): add KnowledgeRepository interface for links and operator reviews"
```

---

## Task 3: In-Memory Knowledge Repository Implementation

**Files:**
- Modify: `aimeat/src/storage/memory.ts` (add KnowledgeRepository implementation)

**Step 1: Read the existing memory.ts file**

Read `aimeat/src/storage/memory.ts` to understand how other repositories are implemented. Look for the Map declarations and method implementations pattern.

**Step 2: Add Map storage and implement methods**

Add to the class fields (where other Maps are declared):

```typescript
private links = new Map<string, MemoryLinkRecord>();       // key: `${source}::${target}`
private reviews = new Map<string, OperatorReviewRecord>();  // key: review.id
```

Add the import for `MemoryLinkRecord` and `OperatorReviewRecord` from `./interface.js`.

Implement the methods:

```typescript
/* ── Knowledge: Memory Links ── */

async createLink(record: MemoryLinkRecord): Promise<MemoryLinkRecord> {
  const key = `${record.source}::${record.target}`;
  this.links.set(key, record);
  return record;
}

async getLink(source: string, target: string): Promise<MemoryLinkRecord | null> {
  return this.links.get(`${source}::${target}`) ?? null;
}

async listLinks(key: string, opts?: { direction?: 'outgoing' | 'incoming' | 'both'; relation?: string }): Promise<MemoryLinkRecord[]> {
  const dir = opts?.direction ?? 'both';
  const results: MemoryLinkRecord[] = [];
  for (const link of this.links.values()) {
    const matchDir = dir === 'both'
      ? (link.source === key || link.target === key)
      : dir === 'outgoing' ? link.source === key : link.target === key;
    if (!matchDir) continue;
    if (opts?.relation && link.relation !== opts.relation) continue;
    results.push(link);
  }
  return results;
}

async deleteLink(source: string, target: string): Promise<boolean> {
  return this.links.delete(`${source}::${target}`);
}

async findBrokenLinks(ownerGaii: string): Promise<MemoryLinkRecord[]> {
  const broken: MemoryLinkRecord[] = [];
  for (const link of this.links.values()) {
    if (link.linked_by !== ownerGaii) continue;
    const sourceExists = await this.getMemory(ownerGaii, link.source);
    const targetExists = await this.getMemory(ownerGaii, link.target);
    if (!sourceExists || !targetExists) broken.push(link);
  }
  return broken;
}

/* ── Knowledge: Operator Reviews ── */

async createReview(record: OperatorReviewRecord): Promise<OperatorReviewRecord> {
  this.reviews.set(record.id, record);
  return record;
}

async listReviews(packageId: string): Promise<OperatorReviewRecord[]> {
  return [...this.reviews.values()].filter(r => r.packageId === packageId);
}

async listAllReviews(opts?: { page?: number; perPage?: number }): Promise<OperatorReviewRecord[]> {
  const all = [...this.reviews.values()].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
  const page = opts?.page ?? 1;
  const perPage = opts?.perPage ?? 20;
  const start = (page - 1) * perPage;
  return all.slice(start, start + perPage);
}
```

**Step 3: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS

**Step 4: Commit**

```bash
git add aimeat/src/storage/memory.ts
git commit -m "feat(knowledge): implement in-memory KnowledgeRepository (links + operator reviews)"
```

---

## Task 4: Knowledge Package JSON Schemas

**Files:**
- Create: `aimeat/src/schemas/knowledge-package.ts`

**Step 1: Create the schema definitions file**

Create `aimeat/src/schemas/knowledge-package.ts` with JSON Schema definitions for each content type. These schemas will be registered via the Schema Locking API.

```typescript
/** JSON Schema definitions for Knowledge Package content types.
 *  Used for schema locking on `packages/{uuid}/{entry}` memory keys. */

export const KNOWLEDGE_CONTENT_TYPES = [
  'idea', 'research', 'plan', 'dataset', 'document',
  'tutorial', 'collection', 'article', 'story', 'fiction',
] as const;

export const ManifestSchema = {
  type: 'object',
  required: ['type', 'name', 'version', 'author', 'content_type', 'tags', 'entries', 'sharing', 'synthesis'],
  properties: {
    type: { type: 'string', const: 'knowledge-package' },
    name: { type: 'string', minLength: 1, maxLength: 200 },
    version: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' },
    author: { type: 'string', minLength: 1 },
    created: { type: 'string', format: 'date-time' },
    updated: { type: 'string', format: 'date-time' },
    content_type: { type: 'string', enum: [...KNOWLEDGE_CONTENT_TYPES] },
    tags: { type: 'array', items: { type: 'string' }, maxItems: 30 },
    language: { type: 'string', minLength: 2, maxLength: 5 },
    maturity: { type: 'string', enum: ['draft', 'review', 'published'] },
    synthesis: {
      type: 'object',
      required: ['level', 'description'],
      properties: {
        level: { type: 'string', enum: ['original', 'assisted', 'synthesized', 'ai-generated'] },
        description: { type: 'string', maxLength: 500 },
        model: { type: 'string' },
      },
    },
    references: {
      type: 'array',
      items: {
        type: 'object',
        required: ['url', 'title', 'accessed', 'verified'],
        properties: {
          url: { type: 'string' },
          title: { type: 'string' },
          accessed: { type: 'string' },
          verified: { type: 'boolean' },
          note: { type: 'string' },
        },
      },
    },
    entries: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        required: ['key', 'title', 'visibility'],
        properties: {
          key: { type: 'string' },
          title: { type: 'string' },
          visibility: { type: 'string', enum: ['private', 'owner', 'public'] },
          schema: { type: 'string' },
        },
      },
    },
    links: {
      type: 'array',
      items: {
        type: 'object',
        required: ['target', 'relation', 'description', 'linked_at'],
        properties: {
          target: { type: 'string' },
          relation: { type: 'string', enum: ['related-to', 'extends', 'derived-from', 'contradicts', 'supersedes', 'references'] },
          description: { type: 'string' },
          linked_at: { type: 'string', format: 'date-time' },
        },
      },
    },
    sharing: {
      type: 'object',
      required: ['catalog_listed', 'allow_clone', 'morsel_price'],
      properties: {
        catalog_listed: { type: 'boolean' },
        allow_clone: { type: 'boolean' },
        license: { type: 'string' },
        morsel_price: { type: 'number', minimum: 0 },
      },
    },
  },
};

/** Content-type-specific entry schemas. Each defines the value shape for entries of that type. */
export const EntrySchemas: Record<string, object> = {
  idea: {
    type: 'object',
    required: ['title', 'description'],
    properties: {
      title: { type: 'string' },
      description: { type: 'string' },
      problem: { type: 'string' },
      proposed_solution: { type: 'string' },
      open_questions: { type: 'array', items: { type: 'string' } },
    },
  },
  research: {
    type: 'object',
    required: ['title', 'summary'],
    properties: {
      title: { type: 'string' },
      summary: { type: 'string' },
      findings: { type: 'array', items: { type: 'string' } },
      sources: { type: 'array', items: { type: 'string' } },
      methodology: { type: 'string' },
      conclusions: { type: 'string' },
    },
  },
  plan: {
    type: 'object',
    required: ['title', 'objective'],
    properties: {
      title: { type: 'string' },
      objective: { type: 'string' },
      steps: { type: 'array', items: { type: 'object' } },
      timeline: { type: 'string' },
      resources: { type: 'array', items: { type: 'string' } },
      risks: { type: 'array', items: { type: 'string' } },
    },
  },
  dataset: {
    type: 'object',
    required: ['title', 'description'],
    properties: {
      title: { type: 'string' },
      description: { type: 'string' },
      format: { type: 'string' },
      fields: { type: 'array', items: { type: 'object' } },
      records: { type: 'array', items: { type: 'object' } },
      source: { type: 'string' },
    },
  },
  document: {
    type: 'object',
    required: ['title', 'body'],
    properties: {
      title: { type: 'string' },
      body: { type: 'string' },
      sections: { type: 'array', items: { type: 'object' } },
      references: { type: 'array', items: { type: 'string' } },
    },
  },
  tutorial: {
    type: 'object',
    required: ['title', 'steps'],
    properties: {
      title: { type: 'string' },
      prerequisites: { type: 'array', items: { type: 'string' } },
      steps: { type: 'array', items: { type: 'object' } },
      expected_outcomes: { type: 'array', items: { type: 'string' } },
    },
  },
  collection: {
    type: 'object',
    required: ['title', 'items'],
    properties: {
      title: { type: 'string' },
      description: { type: 'string' },
      items: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            url: { type: 'string' },
            title: { type: 'string' },
            notes: { type: 'string' },
            tags: { type: 'array', items: { type: 'string' } },
          },
        },
      },
    },
  },
  article: {
    type: 'object',
    required: ['title', 'body'],
    properties: {
      title: { type: 'string' },
      subtitle: { type: 'string' },
      body: { type: 'string' },
      author_bio: { type: 'string' },
      sources: { type: 'array', items: { type: 'string' } },
      category: { type: 'string' },
    },
  },
  story: {
    type: 'object',
    required: ['title', 'body'],
    properties: {
      title: { type: 'string' },
      genre: { type: 'string' },
      body: { type: 'string' },
      chapters: { type: 'array', items: { type: 'object' } },
      characters: { type: 'array', items: { type: 'string' } },
      setting: { type: 'string' },
    },
  },
  fiction: {
    type: 'object',
    required: ['title', 'body'],
    properties: {
      title: { type: 'string' },
      genre: { type: 'string' },
      body: { type: 'string' },
      world_building: { type: 'string' },
      themes: { type: 'array', items: { type: 'string' } },
    },
  },
};
```

**Step 2: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS

**Step 3: Commit**

```bash
git add aimeat/src/schemas/knowledge-package.ts
git commit -m "feat(knowledge): add JSON Schema definitions for all knowledge content types"
```

---

## Task 5: Knowledge Routes — Package Import and CRUD

**Files:**
- Create: `aimeat/src/routes/knowledge.ts`
- Modify: `aimeat/src/server.ts` (mount the router)

**Step 1: Create the knowledge routes file**

Create `aimeat/src/routes/knowledge.ts`:

```typescript
import { Router } from 'express';
import { v4 as uuidv4 } from 'uuid';
import type { AimeatConfig } from '../config.js';
import type { Storage } from '../storage/interface.js';
import { requireAuth, requireRole } from '../auth/middleware.js';
import { success, error } from '../middleware/envelope.js';
import { ManifestSchema } from '../schemas/knowledge-package.js';
import type { KnowledgeManifest, MemoryLinkRecord } from '../storage/interface.js';
import Ajv from 'ajv';

const ajv = new Ajv({ allErrors: true });
const validateManifest = ajv.compile(ManifestSchema);

export function knowledgeRouter(config: AimeatConfig, storage: Storage): Router {
  const router = Router();

  /* ── POST /v1/packages/import — Import a knowledge package from AI Chat output ── */
  router.post('/v1/packages/import', requireAuth(), requireRole('agent'), async (req, res) => {
    const ownerGaii = req.auth!.sub as string;
    const ghii = req.auth!.owner as string;
    const { package: pkg, overrides } = req.body;

    if (!pkg || typeof pkg !== 'object') {
      return res.status(400).json(error(config.nodeId, 'INVALID_PACKAGE', 'Request body must include a "package" object'));
    }

    // Validate manifest structure
    const manifest = pkg as KnowledgeManifest;
    if (!validateManifest(manifest)) {
      return res.status(400).json(error(config.nodeId, 'SCHEMA_VALIDATION', 'Package manifest validation failed', validateManifest.errors));
    }

    const packageId = uuidv4();
    const now = new Date().toISOString();
    const manifestKey = `packages/${packageId}/manifest`;

    // Apply overrides to entries if provided
    if (overrides?.entries) {
      for (const entry of manifest.entries) {
        const entryName = entry.key.split('/').pop() ?? entry.key;
        const entryOverride = overrides.entries[entryName];
        if (entryOverride?.visibility) {
          entry.visibility = entryOverride.visibility;
        }
      }
    }
    if (overrides?.catalog_listed !== undefined) {
      manifest.sharing.catalog_listed = overrides.catalog_listed;
    }

    // Store manifest
    manifest.created = now;
    manifest.updated = now;
    await storage.setMemory({
      key: manifestKey,
      ownerGaii,
      value: manifest,
      visibility: manifest.sharing.catalog_listed ? 'public' : 'owner',
      tags: ['knowledge-package', manifest.content_type, ...manifest.tags],
      ttlHours: null,
      version: 1,
      createdAt: now,
      updatedAt: now,
    });

    // Store entries (entry content is in pkg.entry_data if provided)
    const entryData = (pkg as any).entry_data ?? {};
    const createdEntries: string[] = [];
    for (const entry of manifest.entries) {
      const entryKey = entry.key.startsWith('packages/')
        ? entry.key
        : `packages/${packageId}/${entry.key}`;
      entry.key = entryKey; // Normalize
      const data = entryData[entry.key] ?? entryData[entry.key.split('/').pop() ?? ''] ?? {};
      await storage.setMemory({
        key: entryKey,
        ownerGaii,
        value: data,
        visibility: entry.visibility,
        tags: ['knowledge-entry', manifest.content_type, ...manifest.tags],
        ttlHours: null,
        version: 1,
        createdAt: now,
        updatedAt: now,
      });
      createdEntries.push(entryKey);
    }

    // Create memory links if specified
    for (const link of manifest.links ?? []) {
      await storage.createLink({
        source: manifestKey,
        target: link.target,
        relation: link.relation,
        description: link.description,
        linked_at: link.linked_at || now,
        linked_by: ghii,
      });
    }

    // Create organism consent grant if requested
    if (overrides?.organism_share) {
      await storage.createConsent({
        id: uuidv4(),
        ownerGaii,
        dataPattern: `packages/${packageId}/*`,
        recipient: `organism.${overrides.organism_share}`,
        purpose: 'Knowledge package shared with organism',
        scope: 'private',
        expires: null,
        status: 'active',
        grantedAt: now,
        revokedAt: null,
      });
    }

    res.status(201).json(success(config.nodeId, {
      package_id: packageId,
      manifest_key: manifestKey,
      entries_created: createdEntries.length,
      catalog_listed: manifest.sharing.catalog_listed,
    }, [
      { description: 'View package manifest', method: 'GET', url: `/v1/memory/${encodeURIComponent(manifestKey)}` },
      { description: 'List your packages', method: 'GET', url: '/v1/memory?prefix=packages/&tags=knowledge-package' },
    ]));
  });

  /* ── GET /v1/packages/:id — Get package manifest ── */
  router.get('/v1/packages/:id', async (req, res) => {
    const packageId = req.params.id as string;
    const manifestKey = `packages/${packageId}/manifest`;

    // Try public read first
    const memories = await storage.listMemory('', { prefix: manifestKey, visibility: 'public' });
    const manifest = memories[0];
    if (!manifest) {
      return res.status(404).json(error(config.nodeId, 'NOT_FOUND', 'Package not found or not public'));
    }

    res.json(success(config.nodeId, {
      package_id: packageId,
      manifest: manifest.value,
      tags: manifest.tags,
      created_at: manifest.createdAt,
      updated_at: manifest.updatedAt,
    }));
  });

  /* ── POST /v1/packages/:id/link — Create a link from this package to another memory ── */
  router.post('/v1/packages/:id/link', requireAuth(), requireRole('agent'), async (req, res) => {
    const ownerGaii = req.auth!.sub as string;
    const ghii = req.auth!.owner as string;
    const packageId = req.params.id as string;
    const { target, relation, description } = req.body;

    if (!target || !relation || !description) {
      return res.status(400).json(error(config.nodeId, 'MISSING_FIELDS', 'target, relation, and description are required'));
    }

    const validRelations = ['related-to', 'extends', 'derived-from', 'contradicts', 'supersedes', 'references'];
    if (!validRelations.includes(relation)) {
      return res.status(400).json(error(config.nodeId, 'INVALID_RELATION', `relation must be one of: ${validRelations.join(', ')}`));
    }

    const manifestKey = `packages/${packageId}/manifest`;
    const existing = await storage.getMemory(ownerGaii, manifestKey);
    if (!existing) {
      return res.status(404).json(error(config.nodeId, 'NOT_FOUND', 'Package not found'));
    }

    const now = new Date().toISOString();
    const link: MemoryLinkRecord = {
      source: manifestKey,
      target,
      relation,
      description,
      linked_at: now,
      linked_by: ghii,
    };

    await storage.createLink(link);

    // Also update the manifest's links array
    const manifest = existing.value as KnowledgeManifest;
    manifest.links = manifest.links ?? [];
    manifest.links.push({ target, relation, description, linked_at: now });
    manifest.updated = now;
    existing.value = manifest;
    existing.updatedAt = now;
    existing.version += 1;
    await storage.setMemory(existing);

    res.status(201).json(success(config.nodeId, { link }, [
      { description: 'List package links', method: 'GET', url: `/v1/packages/${packageId}/links` },
    ]));
  });

  /* ── GET /v1/packages/:id/links — List links for a package ── */
  router.get('/v1/packages/:id/links', async (req, res) => {
    const packageId = req.params.id as string;
    const manifestKey = `packages/${packageId}/manifest`;
    const direction = (req.query.direction as string) ?? 'both';
    const relation = req.query.relation as string | undefined;

    const links = await storage.listLinks(manifestKey, {
      direction: direction as 'outgoing' | 'incoming' | 'both',
      relation,
    });

    res.json(success(config.nodeId, { links, count: links.length }));
  });

  /* ── DELETE /v1/packages/:id/link — Delete a link ── */
  router.delete('/v1/packages/:id/link', requireAuth(), requireRole('agent'), async (req, res) => {
    const packageId = req.params.id as string;
    const { target } = req.body;
    if (!target) {
      return res.status(400).json(error(config.nodeId, 'MISSING_FIELDS', 'target is required'));
    }

    const manifestKey = `packages/${packageId}/manifest`;
    const deleted = await storage.deleteLink(manifestKey, target);
    if (!deleted) {
      return res.status(404).json(error(config.nodeId, 'NOT_FOUND', 'Link not found'));
    }

    res.json(success(config.nodeId, { deleted: true }));
  });

  /* ── GET /v1/packages/:id/broken-links — Find broken links ── */
  router.get('/v1/packages/:id/broken-links', requireAuth(), requireRole('agent'), async (req, res) => {
    const ownerGaii = req.auth!.sub as string;
    const broken = await storage.findBrokenLinks(ownerGaii);

    res.json(success(config.nodeId, { broken_links: broken, count: broken.length }));
  });

  /* ── GET /v1/templates/knowledge-packager-human — Get human prompt template ── */
  router.get('/v1/templates/knowledge-packager-human', requireAuth(), async (req, res) => {
    const ghii = req.auth!.owner as string;
    const nodeUrl = config.nodeUrl ?? `http://localhost:${config.port}`;
    const prompt = await storage.getMemory(req.auth!.sub as string, 'templates/knowledge-packager-human');

    if (!prompt) {
      return res.status(404).json(error(config.nodeId, 'NOT_FOUND', 'Human prompt template not installed'));
    }

    // Substitute placeholders
    let text = typeof prompt.value === 'string' ? prompt.value : JSON.stringify(prompt.value);
    text = text.replace(/\{ghii\}/g, ghii);
    text = text.replace(/\{node_url\}/g, nodeUrl);
    text = text.replace(/\{node_id\}/g, config.nodeId);

    res.json(success(config.nodeId, { prompt: text, ghii, node_url: nodeUrl, node_id: config.nodeId }));
  });

  /* ── GET /v1/templates/knowledge-packager-agent — Get agent prompt template ── */
  router.get('/v1/templates/knowledge-packager-agent', requireAuth(), async (req, res) => {
    const ghii = req.auth!.owner as string;
    const gaii = req.auth!.sub as string;
    const nodeUrl = config.nodeUrl ?? `http://localhost:${config.port}`;
    const prompt = await storage.getMemory(gaii, 'templates/knowledge-packager-agent');

    if (!prompt) {
      return res.status(404).json(error(config.nodeId, 'NOT_FOUND', 'Agent prompt template not installed'));
    }

    let text = typeof prompt.value === 'string' ? prompt.value : JSON.stringify(prompt.value);
    text = text.replace(/\{ghii\}/g, ghii);
    text = text.replace(/\{node_url\}/g, nodeUrl);
    text = text.replace(/\{node_id\}/g, config.nodeId);
    text = text.replace(/\{agent_gaii\}/g, gaii);
    text = text.replace(/\{auth_endpoint\}/g, `${nodeUrl}/v1/auth/token`);
    text = text.replace(/\{openapi_spec\}/g, `${nodeUrl}/v1/openapi.yaml`);

    res.json(success(config.nodeId, { prompt: text, ghii, gaii, node_url: nodeUrl, node_id: config.nodeId }));
  });

  return router;
}
```

**Step 2: Mount the router in server.ts**

In `aimeat/src/server.ts`, add the import and mount. Find where other routes are registered (around line 571) and add:

```typescript
import { knowledgeRouter } from './routes/knowledge.js';
// ... in the route mounting section:
app.use(knowledgeRouter(config, storage));
```

**Step 3: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS

**Step 4: Commit**

```bash
git add aimeat/src/routes/knowledge.ts aimeat/src/server.ts
git commit -m "feat(knowledge): add knowledge package routes (import, links, templates)"
```

---

## Task 6: Prompt Templates

**Files:**
- Create: `aimeat/src/prompts/knowledge-packager-human.ts`
- Create: `aimeat/src/prompts/knowledge-packager-agent.ts`

**Step 1: Create the human prompt template**

Create `aimeat/src/prompts/knowledge-packager-human.ts`:

```typescript
/** Human AI Chat prompt template for knowledge packaging.
 *  Placeholders: {ghii}, {node_url}, {node_id}
 *  These are substituted when the user copies the prompt from the Knowledge tab. */

export const KNOWLEDGE_PACKAGER_HUMAN_PROMPT = `# AIMEAT Knowledge Packager — AI Chat Edition

You are helping the user package their knowledge into a structured AIMEAT knowledge package. Follow these instructions precisely.

## Identity (auto-filled — do not change)
- GHII: {ghii}
- Node URL: {node_url}
- Node ID: {node_id}

## Your Task

The user will share content with you — this could be research notes, an idea, a plan, a story, collected links, or anything else. Your job is to:

1. **Ask the user**: "Would you like Quick mode (I make best-guess decisions) or Detailed mode (we go through each option together)?"
2. **Analyze the content** and identify:
   - Content type: idea, research, plan, dataset, document, tutorial, collection, article, story, or fiction
   - Key tags and topics
   - What should be PUBLIC vs PRIVATE (personal details, contacts, financial info → private)
   - How much you (the AI) transformed the content (synthesis level)
   - Any citations or references that should be tracked
3. **Present a structured draft** to the user showing:
   - Proposed package name, content type, tags
   - Each entry with its visibility clearly marked: [PUBLIC] / [PRIVATE] / [SHARED]
   - Synthesis level: original / assisted / synthesized / ai-generated
   - References with verification status
4. **Let the user review and adjust** visibility, tags, structure
5. **Output the final package** as a JSON code block ready to paste into AIMEAT

## Content Types

| Type | Use For |
|------|---------|
| idea | Raw concept, hypothesis, brainstorm |
| research | Investigated topic with sources and findings |
| plan | Steps toward a goal with timeline |
| dataset | Structured data collection |
| document | Long-form written content |
| tutorial | Step-by-step guide |
| collection | Curated list of links/resources |
| article | Opinion piece, analysis, review |
| story | Narrative (fiction or non-fiction) |
| fiction | Creative/imaginative content |

## Synthesis Levels

| Level | When to Use |
|-------|-------------|
| original | User wrote everything; you only formatted it for AIMEAT |
| assisted | User provided the content; you organized, structured, suggested tags |
| synthesized | You combined multiple real sources into new content at user's direction |
| ai-generated | You created most of the content based on a prompt or question |

## CRITICAL RULES

1. **NEVER hallucinate URLs or citations.** If you cannot find or verify a source, say so. Do not invent URLs.
2. **If you lack web search capability**, say: "I don't have web search — I cannot verify sources. All references will be marked as unverified."
3. **Always show visibility clearly.** Every entry must be marked [PUBLIC], [PRIVATE], or [SHARED] before the user confirms.
4. **Never auto-publish.** The user must explicitly confirm before anything is finalized.
5. **Be honest about synthesis level.** If you significantly transformed the input, say so.
6. **The output must include the GHII and node info** so AIMEAT knows where to import it.
7. **For creative types** (story, fiction, article): Citation verification is not required. Focus on structure and tags.

## Output Format

When the user confirms, output EXACTLY this JSON structure as a code block. The user will paste this into their AIMEAT Knowledge tab import box.

\`\`\`json
{
  "aimeat_knowledge_package": true,
  "target_ghii": "{ghii}",
  "target_node": "{node_url}",
  "target_node_id": "{node_id}",
  "package": {
    "type": "knowledge-package",
    "name": "Package Name Here",
    "version": "1.0.0",
    "author": "{ghii}",
    "content_type": "research",
    "tags": ["tag1", "tag2"],
    "language": "en",
    "maturity": "published",
    "synthesis": {
      "level": "assisted",
      "description": "User provided research notes; AI organized into sections and suggested tags"
    },
    "references": [
      {
        "url": "https://example.com/source",
        "title": "Source Title",
        "accessed": "2026-03-07",
        "verified": false,
        "note": "Could not verify — please confirm manually"
      }
    ],
    "entries": [
      {
        "key": "main-findings",
        "title": "Main Findings",
        "visibility": "public"
      },
      {
        "key": "personal-notes",
        "title": "Personal Notes",
        "visibility": "private"
      }
    ],
    "links": [],
    "sharing": {
      "catalog_listed": true,
      "allow_clone": true,
      "license": "CC-BY-4.0",
      "morsel_price": 0
    }
  },
  "entry_data": {
    "main-findings": {
      "title": "Main Findings",
      "summary": "...",
      "findings": ["..."],
      "sources": ["..."]
    },
    "personal-notes": {
      "title": "Personal Notes",
      "body": "..."
    }
  }
}
\`\`\`

## Trust Advisory

Include this notice in your response when presenting the package:
"When others view this package, they will see: 'This knowledge was shared by another user. Verify critical information independently before relying on it.'"

Now, please share the content you'd like to package.`;
```

**Step 2: Create the agent prompt template**

Create `aimeat/src/prompts/knowledge-packager-agent.ts`:

```typescript
/** Agent/OpenClaw prompt template for knowledge packaging.
 *  Placeholders: {ghii}, {node_url}, {node_id}, {agent_gaii}, {auth_endpoint}, {openapi_spec} */

export const KNOWLEDGE_PACKAGER_AGENT_PROMPT = `# AIMEAT Knowledge Packager — Agent Edition

You are an AI agent with direct API access to an AIMEAT node. Your task is to help the user package their knowledge into structured AIMEAT knowledge packages and store them directly via API.

## Identity & Auth (auto-filled)
- GHII: {ghii}
- Node URL: {node_url}
- Node ID: {node_id}
- Agent GAII: {agent_gaii}
- Auth Endpoint: {auth_endpoint}
- OpenAPI Spec: {openapi_spec}

## API Reference

### Memory CRUD
- \`POST {node_url}/v1/memory\` — Create memory entry (body: { key, value, visibility, tags })
- \`PUT {node_url}/v1/memory/:key\` — Update entry
- \`GET {node_url}/v1/memory\` — List entries (?prefix=&tags=&visibility=)
- \`GET {node_url}/v1/memory/search?q=\` — Search memories
- \`GET {node_url}/v1/memory/:key\` — Read single entry
- \`DELETE {node_url}/v1/memory/:key\` — Delete entry

### Knowledge Packages
- \`POST {node_url}/v1/packages/import\` — Import a complete package (body: { package, overrides })
- \`GET {node_url}/v1/packages/:id\` — Get package manifest
- \`POST {node_url}/v1/packages/:id/link\` — Create link (body: { target, relation, description })
- \`GET {node_url}/v1/packages/:id/links\` — List links (?direction=&relation=)

### Consent
- \`POST {node_url}/v1/consent\` — Create consent grant (body: { dataPattern, recipient, purpose, scope })
- \`GET {node_url}/v1/consent\` — List grants

### Schema Locking
- \`PUT {node_url}/v1/memory/:key/schema\` — Set schema for key pattern
- \`GET {node_url}/v1/schemas\` — List all schemas

### Full API Spec
Available at: {openapi_spec}

## Your Task

Same as the human prompt workflow, but with enhanced capabilities:

1. **Ask the user**: Quick mode or Detailed mode?
2. **Analyze content** — identify type, tags, visibility, synthesis level
3. **If you have web search**: Verify all cited sources. Check claims for accuracy. Suggest additional relevant sources. If you CANNOT verify, mark as unverified — NEVER fabricate URLs.
4. **Search existing packages**: \`GET {node_url}/v1/memory?prefix=packages/&tags=knowledge-package\` — find related packages to auto-link
5. **Present draft** to user with [PUBLIC]/[PRIVATE]/[SHARED] markers
6. **User confirms**
7. **Execute API calls**:
   - \`POST /v1/packages/import\` with the complete package
   - Create additional links to related packages found in step 4
8. **Report back**: "Package created with N entries. X public, Y private. Listed in shared catalog. View at: {node_url}/v1/profile#knowledge"

## CRITICAL RULES

1. **Authenticate first** using {auth_endpoint} before making any API calls
2. **NEVER hallucinate URLs or citations.** If you cannot verify, mark as unverified.
3. **Always show visibility clearly** — [PUBLIC] / [PRIVATE] / [SHARED] per entry
4. **Never auto-publish** — user must confirm before you make API calls
5. **Be honest about synthesis level**
6. **Create manifest FIRST, then entries** (use /v1/packages/import which handles this atomically)
7. **Set consent grants AFTER entries exist**
8. **Report back what was created** with direct links

## Content Types & Synthesis Levels

Same as human prompt — see the AIMEAT Knowledge documentation for full list.

## Enhanced Capabilities (agent-only)

- **Deep research**: Search the web for related material to enrich the package
- **Fact-checking**: Verify claims against external sources
- **Link discovery**: Search the node for related packages and auto-suggest links
- **Auto-link**: Create bidirectional links to related packages
- **Schema validation**: Check entries against existing schemas on the node

Now, please share the content you'd like to package.`;
```

**Step 3: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS

**Step 4: Commit**

```bash
git add aimeat/src/prompts/knowledge-packager-human.ts aimeat/src/prompts/knowledge-packager-agent.ts
git commit -m "feat(knowledge): add human and agent prompt templates for knowledge packaging"
```

---

## Task 7: Seed Prompt Templates on Server Start

**Files:**
- Create: `aimeat/src/services/knowledge.ts`
- Modify: `aimeat/src/server.ts` (call seed function on startup)

**Step 1: Create the knowledge service**

Create `aimeat/src/services/knowledge.ts`:

```typescript
import type { Storage } from '../storage/interface.js';
import { KNOWLEDGE_PACKAGER_HUMAN_PROMPT } from '../prompts/knowledge-packager-human.js';
import { KNOWLEDGE_PACKAGER_AGENT_PROMPT } from '../prompts/knowledge-packager-agent.js';

/** Seed the knowledge packager prompt templates into memory if they don't exist.
 *  Called once at server startup. Uses the first owner's agent GAII as the owner. */
export async function seedKnowledgeTemplates(storage: Storage, systemGaii: string): Promise<void> {
  const now = new Date().toISOString();

  const humanKey = 'templates/knowledge-packager-human';
  const agentKey = 'templates/knowledge-packager-agent';

  const existingHuman = await storage.getMemory(systemGaii, humanKey);
  if (!existingHuman) {
    await storage.setMemory({
      key: humanKey,
      ownerGaii: systemGaii,
      value: KNOWLEDGE_PACKAGER_HUMAN_PROMPT,
      visibility: 'public',
      tags: ['template', 'knowledge', 'prompt', 'human'],
      ttlHours: null,
      version: 1,
      createdAt: now,
      updatedAt: now,
    });
  }

  const existingAgent = await storage.getMemory(systemGaii, agentKey);
  if (!existingAgent) {
    await storage.setMemory({
      key: agentKey,
      ownerGaii: systemGaii,
      value: KNOWLEDGE_PACKAGER_AGENT_PROMPT,
      visibility: 'public',
      tags: ['template', 'knowledge', 'prompt', 'agent'],
      ttlHours: null,
      version: 1,
      createdAt: now,
      updatedAt: now,
    });
  }
}
```

**Step 2: Call from server startup**

In `aimeat/src/server.ts`, find where the server starts listening (after storage initialization). Add:

```typescript
import { seedKnowledgeTemplates } from './services/knowledge.js';

// After storage is initialized and first owner exists:
// seedKnowledgeTemplates(storage, firstOwnerAgentGaii);
// Note: The exact integration point depends on how the server bootstraps.
// Look for where stats or directory service are initialized — seed templates there.
```

**Step 3: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS

**Step 4: Commit**

```bash
git add aimeat/src/services/knowledge.ts aimeat/src/server.ts
git commit -m "feat(knowledge): add knowledge service with prompt template seeding"
```

---

## Task 8: E2E Tests for Phase 1

**Files:**
- Create: `aimeat/test/e2e-knowledge.ts`

**Step 1: Create the test file**

Create `aimeat/test/e2e-knowledge.ts` following the same pattern as `test/e2e-full.ts`:

```typescript
import * as ed from '@noble/ed25519';
import { createHash } from 'node:crypto';

ed.etc.sha512Sync = (...m: Uint8Array[]) =>
  new Uint8Array(createHash('sha512').update(Buffer.concat(m)).digest());

const BASE = process.env.E2E_BASE ?? 'http://localhost:40251';
const NODE = process.env.E2E_NODE_ID ?? 'aimeat-local-001-dev';

let passed = 0;
let failed = 0;

async function test(name: string, fn: () => Promise<void>) {
  try { await fn(); passed++; console.log(`  ✅ ${name}`); }
  catch (err: any) { failed++; console.error(`  ❌ ${name}: ${err.message}`); }
}

function assert(cond: boolean, msg: string) { if (!cond) throw new Error(msg); }

async function json(path: string, opts: RequestInit = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...opts,
    headers: { 'Content-Type': 'application/json', ...opts.headers },
  });
  const ct = res.headers.get('content-type') ?? '';
  const body = ct.includes('json') ? await res.json() : { _raw: await res.text(), _ct: ct };
  return { status: res.status, body, headers: res.headers };
}

// Test owner/agent setup — reuse the pattern from e2e-full.ts
let ownerToken = '';
let agentToken = '';
let testGaii = '';

async function setup() {
  // Register test owner
  const priv = ed.utils.randomPrivateKey();
  const pub = await ed.getPublicKeyAsync(priv);
  const pubHex = Buffer.from(pub).toString('hex');

  const reg = await json('/v1/owners', {
    method: 'POST',
    body: JSON.stringify({ name: 'knowledge-test-owner', publicKey: pubHex }),
  });

  // Register test agent
  const agentReg = await json('/v1/agents', {
    method: 'POST',
    body: JSON.stringify({ name: 'knowledge-test-agent', ownerName: 'knowledge-test-owner' }),
    headers: { Authorization: `Bearer ${reg.body.data?.token}` },
  });

  ownerToken = reg.body.data?.token;
  agentToken = agentReg.body.data?.token;
  testGaii = agentReg.body.data?.gaii;
}

async function runTests() {
  console.log('\n🧠 Knowledge System E2E Tests\n');

  console.log('Phase 0: Setup');
  await setup();

  console.log('\nPhase 1: Package Import');

  await test('Import a valid knowledge package', async () => {
    const { status, body } = await json('/v1/packages/import', {
      method: 'POST',
      headers: { Authorization: `Bearer ${agentToken}` },
      body: JSON.stringify({
        package: {
          type: 'knowledge-package',
          name: 'Test Research Package',
          version: '1.0.0',
          author: 'knowledge-test-owner',
          content_type: 'research',
          tags: ['test', 'solar'],
          language: 'en',
          maturity: 'published',
          synthesis: { level: 'assisted', description: 'AI helped structure notes' },
          references: [{ url: 'https://example.com', title: 'Example', accessed: '2026-03-07', verified: false }],
          entries: [
            { key: 'findings', title: 'Main Findings', visibility: 'public' },
            { key: 'private-notes', title: 'Private Notes', visibility: 'private' },
          ],
          links: [],
          sharing: { catalog_listed: true, allow_clone: true, morsel_price: 0 },
        },
        entry_data: {
          findings: { title: 'Main Findings', summary: 'Test findings', findings: ['fact 1'] },
          'private-notes': { title: 'Private Notes', body: 'Secret stuff' },
        },
      }),
    });
    assert(status === 201, `Expected 201, got ${status}`);
    assert(body.data?.package_id, 'Missing package_id');
    assert(body.data?.entries_created === 2, `Expected 2 entries, got ${body.data?.entries_created}`);
  });

  await test('Reject invalid package (missing required fields)', async () => {
    const { status } = await json('/v1/packages/import', {
      method: 'POST',
      headers: { Authorization: `Bearer ${agentToken}` },
      body: JSON.stringify({ package: { name: 'incomplete' } }),
    });
    assert(status === 400, `Expected 400, got ${status}`);
  });

  console.log('\nPhase 1: Memory Links');

  await test('Create and list memory links', async () => {
    // First create a package to link from
    const { body: pkg1 } = await json('/v1/packages/import', {
      method: 'POST',
      headers: { Authorization: `Bearer ${agentToken}` },
      body: JSON.stringify({
        package: {
          type: 'knowledge-package', name: 'Link Source', version: '1.0.0',
          author: 'knowledge-test-owner', content_type: 'idea', tags: ['test'],
          language: 'en', maturity: 'draft',
          synthesis: { level: 'original', description: 'User created' },
          references: [], entries: [{ key: 'content', title: 'Content', visibility: 'public' }],
          links: [], sharing: { catalog_listed: false, allow_clone: false, morsel_price: 0 },
        },
        entry_data: { content: { title: 'Content', description: 'Test' } },
      }),
    });

    const pkgId = pkg1.data.package_id;

    // Create a link
    const { status: linkStatus } = await json(`/v1/packages/${pkgId}/link`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${agentToken}` },
      body: JSON.stringify({
        target: 'some/other/memory',
        relation: 'related-to',
        description: 'Test link',
      }),
    });
    assert(linkStatus === 201, `Expected 201 for link, got ${linkStatus}`);

    // List links
    const { status: listStatus, body: listBody } = await json(`/v1/packages/${pkgId}/links`);
    assert(listStatus === 200, `Expected 200 for list, got ${listStatus}`);
    assert(listBody.data.count >= 1, 'Expected at least 1 link');
  });

  console.log('\nPhase 1: Prompt Templates');

  await test('Retrieve human prompt template', async () => {
    const { status, body } = await json('/v1/templates/knowledge-packager-human', {
      headers: { Authorization: `Bearer ${agentToken}` },
    });
    // May be 404 if not seeded yet — that's acceptable for now
    assert(status === 200 || status === 404, `Expected 200 or 404, got ${status}`);
    if (status === 200) {
      assert(typeof body.data.prompt === 'string', 'Expected prompt to be a string');
      assert(body.data.ghii, 'Expected ghii in response');
    }
  });

  // Cleanup
  console.log('\nCleanup');
  await json('/v1/owners/knowledge-test-owner?cascade=true', {
    method: 'DELETE',
    headers: { Authorization: `Bearer ${ownerToken}` },
  });

  console.log(`\n📊 Results: ${passed} passed, ${failed} failed\n`);
  process.exit(failed > 0 ? 1 : 0);
}

runTests().catch(err => { console.error(err); process.exit(1); });
```

**Step 2: Run the tests** (server must be running on port 40251)

Run: `cd aimeat && npx tsx test/e2e-knowledge.ts`
Expected: Tests pass (some may be 404 for templates if not seeded — that's acceptable for Phase 1)

**Step 3: Commit**

```bash
git add aimeat/test/e2e-knowledge.ts
git commit -m "test(knowledge): add Phase 1 E2E tests for import, links, and templates"
```

---

## Task 9: i18n Keys for Knowledge System

**Files:**
- Modify: `aimeat/locales/en.json` (add `knowledge` section)
- Modify: `aimeat/locales/fi.json` (add `knowledge` section)

**Step 1: Add English translations**

Add a `knowledge` top-level key to `en.json`:

```json
"knowledge": {
  "tabLabel": "Knowledge",
  "actionBar": {
    "copyHumanPrompt": "Copy Prompt for AI Chat",
    "copyAgentPrompt": "Copy Prompt for OpenClaw",
    "description": "Use these prompts to package research, ideas, or any content into structured knowledge. Paste the prompt into your AI Chat or give it to your OpenClaw agent."
  },
  "import": {
    "title": "Import Knowledge Package",
    "placeholder": "Paste what your AI Chat produced here",
    "uploadFile": "Upload File",
    "agentNote": "Agents upload automatically — you don't need to paste anything if you used OpenClaw.",
    "preview": "Package Preview",
    "ghiiConfirm": "This will be stored under: {ghii}",
    "ghiiMismatch": "This package was created for {ghii}. Import anyway as your own?",
    "catalogToggle": "List in shared knowledge catalog?",
    "organismShare": "Share with an organism?",
    "confirmImport": "Confirm Import",
    "willCreate": "Will create {entries} memories and {consents} consent grants",
    "success": "Package imported successfully!",
    "error": "Failed to import package"
  },
  "myKnowledge": {
    "title": "My Knowledge",
    "empty": "No knowledge packages yet. Use the prompts above to create your first package!",
    "cloned": "Cloned",
    "draft": "Draft",
    "entries": "{count} entries",
    "clones": "{count} clones",
    "viewEdit": "View / Edit",
    "shareSettings": "Share Settings",
    "export": "Export"
  },
  "sharedWithMe": {
    "title": "Shared With Me",
    "empty": "No one has shared knowledge packages with you yet.",
    "cloneToMine": "Clone to My Knowledge",
    "viewOriginal": "View Original"
  },
  "organisms": {
    "title": "Knowledge Organisms",
    "empty": "You're not part of any knowledge-sharing organisms.",
    "contribute": "Contribute",
    "packages": "{count} packages",
    "members": "{count} members"
  },
  "discover": {
    "title": "Discover",
    "search": "Search knowledge...",
    "empty": "No shared knowledge packages found.",
    "cloneToMine": "Clone to My Knowledge",
    "viewDetails": "View Details",
    "trustAdvisory": "Verify critical information independently before relying on it."
  },
  "contentTypes": {
    "idea": "Idea",
    "research": "Research",
    "plan": "Plan",
    "dataset": "Dataset",
    "document": "Document",
    "tutorial": "Tutorial",
    "collection": "Collection",
    "article": "Article",
    "story": "Story",
    "fiction": "Fiction"
  },
  "synthesis": {
    "original": "Original",
    "assisted": "AI Assisted",
    "synthesized": "AI Synthesized",
    "ai-generated": "AI Generated"
  },
  "maturity": {
    "draft": "Draft",
    "review": "In Review",
    "published": "Published"
  },
  "visibility": {
    "public": "Public",
    "private": "Private",
    "owner": "Shared"
  },
  "operator": {
    "tabLabel": "Knowledge",
    "review": "Review",
    "reasons": {
      "routine_review": "Routine review",
      "legal_compliance": "Legal compliance check",
      "community_report": "Community report",
      "content_quality": "Content quality check",
      "storage_issue": "Storage issue",
      "custom": "Custom"
    },
    "actions": {
      "approve": "Approve",
      "flag": "Flag",
      "delist": "Delist",
      "restrict": "Restrict",
      "note": "Add Note"
    },
    "reviewedBy": "Reviewed by {operator} on {date}",
    "reason": "Reason: {reason}"
  }
}
```

**Step 2: Add Finnish translations**

Add the corresponding `knowledge` section to `fi.json` with Finnish translations. (Follow the same structure — translate values, keep keys identical.)

**Step 3: Run type check**

Run: `cd aimeat && npx tsc --noEmit`
Expected: PASS (JSON files don't affect TypeScript compilation, but verify no syntax errors)

**Step 4: Commit**

```bash
git add aimeat/locales/en.json aimeat/locales/fi.json
git commit -m "feat(knowledge): add i18n translations for Knowledge System (en + fi)"
```

---

## Phase 1 Complete

After completing all 9 tasks, you have:
- All Knowledge System type definitions in the storage interface
- KnowledgeRepository interface + in-memory implementation
- Knowledge package routes (import, links, templates)
- JSON Schema definitions for all 10 content types
- Human and Agent prompt templates with placeholder substitution
- Knowledge service with template seeding
- E2E tests for import, links, and templates
- i18n translations for all Knowledge UI strings

**Next:** [Phase 2: Knowledge Tab UI](02-knowledge-tab.md)
