Skip to main content
Glama

decision_memory

Record, query, and manage architectural decisions across sessions to maintain consistent design rationale.

Instructions

Record, query, and manage architectural decisions across sessions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The executeDecisionMemory function is the main handler for the decision_memory tool. It handles five actions: record (inserts into developer_decisions table with AWE bridge), list (queries with filters), check_alignment (detects conflicts with existing decisions), update (modifies fields), and supersede (marks old as superseded linked to new).
    export function executeDecisionMemory(
      db: FourDADatabase,
      params: DecisionMemoryParams
    ): object {
      const rawDb = db.getRawDb();
    
      switch (params.action) {
        case "record": {
          if (!params.subject || !params.decision) {
            return {
              error: "subject and decision are required for record action",
            };
          }
    
          const stmt = rawDb.prepare(
            `INSERT INTO developer_decisions
               (decision_type, subject, decision, rationale,
                alternatives_rejected, context_tags, confidence, status)
             VALUES (?, ?, ?, ?, ?, ?, ?, 'active')`
          );
    
          const result = stmt.run(
            params.decision_type || "tech_choice",
            params.subject,
            params.decision,
            params.rationale || null,
            JSON.stringify(params.alternatives_rejected || []),
            JSON.stringify(params.context_tags || []),
            params.confidence ?? 0.8
          );
    
          // Bridge to AWE Wisdom Graph: forward decision for wisdom tracking.
          // Non-blocking — AWE sync is best-effort, doesn't affect 4DA recording.
          try {
            const aweBin = findAweBinary();
            if (aweBin) {
              // Sanitize inputs before passing to external process
              const safeSubject = (params.subject || "").slice(0, 500).replace(/\0/g, '');
              const safeDecision = (params.decision || "").slice(0, 2000).replace(/\0/g, '');
              const query = `${safeSubject}: ${safeDecision}`;
              const domain = mapDecisionTypeToDomain(params.decision_type || "tech_choice");
              // AWE bridge: persist decisions to Wisdom Graph for compounding
              // Context file auto-assembled by AWE MCP server's buildContextFile()
              const contextFile = process.platform === "win32"
                ? `${process.env.APPDATA || ""}\\awe\\identity.json`
                : `${process.env.HOME || ""}/.local/share/awe/identity.json`;
              const contextArgs = existsSync(contextFile) ? ["--context_file", contextFile] : [];
              const child = execFile(aweBin, [
                "transmute", query,
                "--domain", domain,
                "--json",
                ...contextArgs,
              ], { timeout: 30_000 }, () => {
                // Callback required — ignore result (fire-and-forget)
              });
              child.unref();
            }
          } catch {
            // AWE bridge is optional
          }
    
          return {
            success: true,
            id: result.lastInsertRowid,
            message: `Decision recorded: ${params.subject}`,
          };
        }
    
        case "list": {
          let sql = `SELECT id, decision_type, subject, decision, rationale,
                            alternatives_rejected, context_tags, confidence,
                            status, superseded_by, created_at, updated_at
                     FROM developer_decisions WHERE 1=1`;
          const sqlParams: (string | number)[] = [];
    
          if (params.filter_type) {
            sql += ` AND decision_type = ?`;
            sqlParams.push(params.filter_type);
          }
          if (params.filter_status) {
            sql += ` AND status = ?`;
            sqlParams.push(params.filter_status);
          }
    
          sql += ` ORDER BY updated_at DESC LIMIT ?`;
          sqlParams.push(params.limit || 20);
    
          const rows = rawDb
            .prepare(sql)
            .all(...sqlParams) as DecisionRow[];
    
          return {
            decisions: rows.map(parseDecisionRow),
            count: rows.length,
          };
        }
    
        case "check_alignment": {
          if (!params.technology) {
            return {
              error: "technology is required for check_alignment action",
            };
          }
    
          const search = `%${params.technology.toLowerCase()}%`;
          const rows = rawDb
            .prepare(
              `SELECT id, decision_type, subject, decision, rationale,
                      alternatives_rejected, context_tags, confidence,
                      status, created_at
               FROM developer_decisions
               WHERE status = 'active'
                 AND (LOWER(subject) LIKE ?
                   OR LOWER(context_tags) LIKE ?
                   OR LOWER(alternatives_rejected) LIKE ?)`
            )
            .all(search, search, search) as DecisionRow[];
    
          const conflicts: {
            decision_id: number;
            subject: string;
            reason: string;
          }[] = [];
          const relevant: ReturnType<typeof parseDecisionRow>[] = [];
    
          for (const row of rows) {
            const alts: string[] = JSON.parse(
              row.alternatives_rejected || "[]"
            );
            const isRejected = alts.some((alt) =>
              alt.toLowerCase().includes(params.technology!.toLowerCase())
            );
    
            if (isRejected) {
              conflicts.push({
                decision_id: row.id,
                subject: row.subject,
                reason: `'${params.technology}' was rejected in favor of '${row.decision}' (rationale: ${row.rationale || "none"})`,
              });
            }
    
            relevant.push(parseDecisionRow(row));
          }
    
          return {
            aligned: conflicts.length === 0,
            relevant_decisions: relevant,
            conflicts,
            confidence:
              relevant.length > 0
                ? Math.max(...relevant.map((r) => r.confidence))
                : 0.5,
          };
        }
    
        case "update": {
          if (!params.id) {
            return { error: "id is required for update action" };
          }
    
          const sets: string[] = [];
          const values: (string | number)[] = [];
    
          if (params.new_decision) {
            sets.push("decision = ?");
            values.push(params.new_decision);
          }
          if (params.new_rationale) {
            sets.push("rationale = ?");
            values.push(params.new_rationale);
          }
          if (params.new_status) {
            sets.push("status = ?");
            values.push(params.new_status);
          }
          if (params.new_confidence !== undefined) {
            sets.push("confidence = ?");
            values.push(params.new_confidence);
          }
    
          if (sets.length === 0) {
            return { error: "No fields to update" };
          }
    
          sets.push("updated_at = datetime('now')");
          values.push(params.id);
    
          rawDb
            .prepare(
              `UPDATE developer_decisions SET ${sets.join(", ")} WHERE id = ?`
            )
            .run(...values);
    
          return {
            success: true,
            message: `Decision ${params.id} updated`,
          };
        }
    
        case "supersede": {
          if (!params.old_id || !params.new_id) {
            return {
              error: "old_id and new_id are required for supersede action",
            };
          }
    
          rawDb
            .prepare(
              `UPDATE developer_decisions
               SET status = 'superseded',
                   superseded_by = ?,
                   updated_at = datetime('now')
               WHERE id = ?`
            )
            .run(params.new_id, params.old_id);
    
          return {
            success: true,
            message: `Decision ${params.old_id} superseded by ${params.new_id}`,
          };
        }
    
        default:
          return { error: `Unknown action: ${params.action}` };
      }
    }
  • The decisionMemoryTool object defines the tool's input schema including all properties for each action (record, list, check_alignment, update, supersede) with their types, enums, and descriptions.
    export const decisionMemoryTool = {
      name: "decision_memory",
      description:
        "Manage developer decisions. Actions: record (create new decision), list (query decisions), check_alignment (check if tech/pattern conflicts with decisions), update (modify a decision), supersede (replace old decision with new one).",
      inputSchema: {
        type: "object" as const,
        properties: {
          action: {
            type: "string",
            enum: ["record", "list", "check_alignment", "update", "supersede"],
            description: "Action to perform",
          },
          decision_type: {
            type: "string",
            enum: [
              "tech_choice",
              "architecture",
              "workflow",
              "pattern",
              "dependency",
            ],
            description: "Type of decision (for record)",
          },
          subject: {
            type: "string",
            description:
              "Subject of the decision (for record, check_alignment)",
          },
          decision: {
            type: "string",
            description: "The decision text (for record)",
          },
          rationale: {
            type: "string",
            description: "Why this decision was made (for record)",
          },
          alternatives_rejected: {
            type: "array",
            items: { type: "string" },
            description:
              "Alternatives that were considered and rejected (for record)",
          },
          context_tags: {
            type: "array",
            items: { type: "string" },
            description: "Tags for categorization (for record)",
          },
          confidence: {
            type: "number",
            description: "Confidence level 0-1 (for record)",
          },
          filter_type: {
            type: "string",
            description: "Filter by decision type (for list)",
          },
          filter_status: {
            type: "string",
            description:
              "Filter by status: active, superseded, reconsidering (for list)",
          },
          limit: {
            type: "number",
            description: "Max results to return (for list)",
          },
          technology: {
            type: "string",
            description: "Technology to check alignment for",
          },
          pattern: {
            type: "string",
            description: "Pattern to check alignment for",
          },
          id: {
            type: "number",
            description: "Decision ID (for update)",
          },
          new_decision: {
            type: "string",
            description: "Updated decision text (for update)",
          },
          new_rationale: {
            type: "string",
            description: "Updated rationale (for update)",
          },
          new_status: {
            type: "string",
            enum: ["active", "superseded", "reconsidering"],
            description: "Updated status (for update)",
          },
          new_confidence: {
            type: "number",
            description: "Updated confidence (for update)",
          },
          old_id: {
            type: "number",
            description: "ID of decision to supersede (for supersede)",
          },
          new_id: {
            type: "number",
            description:
              "ID of new decision that replaces old (for supersede)",
          },
        },
        required: ["action"],
      },
    };
  • DecisionMemoryParams interface defines the TypeScript type for all parameters the tool accepts, organized by action.
    export interface DecisionMemoryParams {
      action: "record" | "list" | "check_alignment" | "update" | "supersede";
      // For record
      decision_type?:
        | "tech_choice"
        | "architecture"
        | "workflow"
        | "pattern"
        | "dependency";
      subject?: string;
      decision?: string;
      rationale?: string;
      alternatives_rejected?: string[];
      context_tags?: string[];
      confidence?: number;
      // For list
      filter_type?: string;
      filter_status?: string;
      limit?: number;
      // For check_alignment
      technology?: string;
      pattern?: string;
      // For update
      id?: number;
      new_decision?: string;
      new_rationale?: string;
      new_status?: "active" | "superseded" | "reconsidering";
      new_confidence?: number;
      // For supersede
      old_id?: number;
      new_id?: number;
    }
  • Registration in the dispatch map: 'decision_memory' mapped to executeDecisionMemory function.
    // Decisions
    decision_memory: executeDecisionMemory,
  • Schema registry entry for 'decision_memory' tool, marked as standalone in the 'decisions' category.
    // --- Decisions (standalone) ---
    decision_memory: {
      summary: "Record, query, and manage architectural decisions across sessions",
      schemaFile: "decision-memory.json",
      category: "decisions",
      tags: ["decisions", "memory", "record", "architecture"],
      standalone: true,
    },
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It mentions basic actions (record, query, manage) but does not disclose traits like whether decisions can be deleted, updated, or how persistence works. Lacks detail on side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence with three clear verbs front-loaded. No unnecessary words. Every part adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with no parameters and no output schema, the description sufficiently covers purpose and scope. Could be enhanced by specifying what 'manage' includes (e.g., update, delete).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters in schema, and schema description coverage is 100% (trivially). Per guidelines, 0 params baseline is 4. Description does not add param info but none needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description specifies verb+resource: 'Record, query, and manage architectural decisions' and scope 'across sessions'. It clearly distinguishes from siblings like 'agent_memory' which deals with general memory, and 'check_decision_alignment' which is a specific check.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool vs siblings like 'agent_memory' or 'check_decision_alignment'. The description only states what it does without context of alternatives or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/runyourempire/4DA'

If you have feedback or need assistance with the MCP directory API, please join our Discord server