From ae2f0f1c819585f534aae32bbcf01c47881a288d Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Thu, 19 Mar 2026 20:30:50 +0200 Subject: [PATCH] kin: KIN-DOCS-004-backend_dev --- agents/prompts/architect.md | 1 + agents/prompts/backend_dev.md | 1 + agents/prompts/department_head.md | 11 ++++++++++- agents/prompts/frontend_dev.md | 1 + agents/runner.py | 33 +++++++++++++++++++++++-------- agents/specialists.yaml | 14 +++++++++++++ core/db.py | 8 ++++++++ core/models.py | 9 ++++++--- 8 files changed, 66 insertions(+), 12 deletions(-) diff --git a/agents/prompts/architect.md b/agents/prompts/architect.md index e5780e1..e648385 100644 --- a/agents/prompts/architect.md +++ b/agents/prompts/architect.md @@ -15,6 +15,7 @@ You receive: **Normal mode** (default): +0. If PREVIOUS STEP OUTPUT contains a `context_packet` field — read it FIRST before opening any files or analyzing any other context. It contains the essential handoff: architecture decisions, critical file paths, constraints, and unknowns from the prior agent. 1. Read `DESIGN.md`, `core/models.py`, `core/db.py`, `agents/runner.py`, and any MODULES files relevant to the task 2. Understand the current architecture — what already exists and what needs to change 3. Design the solution: data model, interfaces, component interactions diff --git a/agents/prompts/backend_dev.md b/agents/prompts/backend_dev.md index 3b4a97f..97b7218 100644 --- a/agents/prompts/backend_dev.md +++ b/agents/prompts/backend_dev.md @@ -12,6 +12,7 @@ You receive: ## Working Mode +0. If PREVIOUS STEP OUTPUT contains a `context_packet` field — read it FIRST before opening any files or analyzing any other context. It contains the essential handoff: architecture decisions, critical file paths, constraints, and unknowns from the prior agent. 1. Read all relevant backend files before making any changes 2. Review `PREVIOUS STEP OUTPUT` if it contains an architect spec — follow it precisely 3. Implement the feature or fix as described in the task brief diff --git a/agents/prompts/department_head.md b/agents/prompts/department_head.md index 7f1a1f2..4da74f3 100644 --- a/agents/prompts/department_head.md +++ b/agents/prompts/department_head.md @@ -28,6 +28,7 @@ You receive: - Handoff notes clarity — the next department must be able to start without asking questions - Previous department handoff — build on their work, don't repeat it - Sub-pipeline length — keep it SHORT, 1-4 steps maximum +- Produce a `context_packet` with exactly 5 fields: `architecture_notes` (string — key arch decisions made), `key_files` (array of file paths critical for the next agent), `constraints` (array of hard technical/business limits discovered), `unknowns` (array of open risks or unresolved questions), `handoff_for` (string — role name of the first worker in sub_pipeline). In the brief for the FIRST worker in sub_pipeline, include this sentence verbatim: «IMPORTANT: Read the `context_packet` field in PREVIOUS STEP OUTPUT FIRST, before any other section or file.» **Department-specific guidance:** @@ -46,6 +47,7 @@ You receive: - Each worker brief is self-contained — no "see above" references - Artifacts list is complete and specific - Handoff notes are actionable for the next department +- `context_packet` is present with all 5 required fields; `handoff_for` is non-empty and matches the role in `sub_pipeline[0]`; first worker brief contains explicit instruction to read `context_packet` first ## Return Format @@ -72,7 +74,14 @@ Return ONLY valid JSON (no markdown, no explanation): "schemas": [], "notes": "Added feature with full test coverage. All tests pass." }, - "handoff_notes": "Backend implementation complete. Tests passing. Frontend needs to call POST /api/feature with {field: value} body." + "handoff_notes": "Backend implementation complete. Tests passing. Frontend needs to call POST /api/feature with {field: value} body.", + "context_packet": { + "architecture_notes": "Used existing models.py pattern, no ORM, raw sqlite3", + "key_files": ["core/models.py", "web/api.py"], + "constraints": ["All DB columns must have DEFAULT values", "No new Python deps"], + "unknowns": ["Frontend integration not yet verified"], + "handoff_for": "backend_dev" + } } ``` diff --git a/agents/prompts/frontend_dev.md b/agents/prompts/frontend_dev.md index 3d2f29b..cea39a7 100644 --- a/agents/prompts/frontend_dev.md +++ b/agents/prompts/frontend_dev.md @@ -12,6 +12,7 @@ You receive: ## Working Mode +0. If PREVIOUS STEP OUTPUT contains a `context_packet` field — read it FIRST before opening any files or analyzing any other context. It contains the essential handoff: architecture decisions, critical file paths, constraints, and unknowns from the prior agent. 1. Read all relevant frontend files before making any changes 2. Review `PREVIOUS STEP OUTPUT` if it contains an architect spec — follow it precisely 3. Implement the feature or fix as described in the task brief diff --git a/agents/runner.py b/agents/runner.py index 9ada7f8..066b77d 100644 --- a/agents/runner.py +++ b/agents/runner.py @@ -1347,14 +1347,29 @@ def _execute_department_head_step( role = step["role"] dept_name = role.replace("_head", "") - # Build initial context for workers: dept head's plan + artifacts - dept_plan_context = json.dumps({ - "department_head_plan": { - "department": dept_name, - "artifacts": parsed.get("artifacts", {}), - "handoff_notes": parsed.get("handoff_notes", ""), - }, - }, ensure_ascii=False) + # Extract context_packet (KIN-DOCS-004): fail-open if missing + context_packet = parsed.get("context_packet") + if context_packet is None and parent_pipeline_id: + try: + models.write_log( + conn, parent_pipeline_id, + f"Dept {step['role']}: context_packet missing from output — handoff quality degraded", + level="WARN", + extra={"role": step["role"]}, + ) + except Exception: + pass + + # Build initial context for workers: context_packet first, then dept head's plan + dept_plan_context_dict: dict = {} + if context_packet is not None: + dept_plan_context_dict["context_packet"] = context_packet + dept_plan_context_dict["department_head_plan"] = { + "department": dept_name, + "artifacts": parsed.get("artifacts", {}), + "handoff_notes": parsed.get("handoff_notes", ""), + } + dept_plan_context = json.dumps(dept_plan_context_dict, ensure_ascii=False) # KIN-084: log sub-pipeline start if parent_pipeline_id: @@ -1429,6 +1444,7 @@ def _execute_department_head_step( decisions_made=decisions_made, blockers=[], status=handoff_status, + context_packet=context_packet, ) except Exception: pass # Handoff save errors must never block pipeline @@ -1438,6 +1454,7 @@ def _execute_department_head_step( "from_department": dept_name, "handoff_notes": parsed.get("handoff_notes", ""), "artifacts": parsed.get("artifacts", {}), + "context_packet": context_packet, "sub_pipeline_summary": { "steps_completed": sub_result.get("steps_completed", 0), "success": sub_result.get("success", False), diff --git a/agents/specialists.yaml b/agents/specialists.yaml index cd3af24..6bef8bf 100644 --- a/agents/specialists.yaml +++ b/agents/specialists.yaml @@ -181,6 +181,8 @@ specialists: context_rules: decisions: all modules: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" frontend_head: name: "Frontend Department Head" @@ -193,6 +195,8 @@ specialists: context_rules: decisions: all modules: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" qa_head: name: "QA Department Head" @@ -204,6 +208,8 @@ specialists: permissions: read_only context_rules: decisions: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" security_head: name: "Security Department Head" @@ -215,6 +221,8 @@ specialists: permissions: read_only context_rules: decisions_category: security + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" infra_head: name: "Infrastructure Department Head" @@ -226,6 +234,8 @@ specialists: permissions: read_only context_rules: decisions: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" knowledge_synthesizer: name: "Knowledge Synthesizer" @@ -252,6 +262,8 @@ specialists: permissions: read_only context_rules: decisions: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" marketing_head: name: "Marketing Department Head" @@ -264,6 +276,8 @@ specialists: context_rules: decisions: all modules: all + output_schema: + context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }" # Departments — PM uses these when routing complex cross-domain tasks to department heads departments: diff --git a/core/db.py b/core/db.py index 5d8e47b..e795fb6 100644 --- a/core/db.py +++ b/core/db.py @@ -165,6 +165,7 @@ CREATE TABLE IF NOT EXISTS department_handoffs ( artifacts JSON, decisions_made JSON, blockers JSON, + context_packet JSON DEFAULT NULL, status TEXT DEFAULT 'pending', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); @@ -703,6 +704,13 @@ def _migrate(conn: sqlite3.Connection): """) conn.commit() + # Add context_packet column to department_handoffs (KIN-DOCS-004) + if "department_handoffs" in existing_tables: + handoff_cols = {r[1] for r in conn.execute("PRAGMA table_info(department_handoffs)").fetchall()} + if "context_packet" not in handoff_cols: + conn.execute("ALTER TABLE department_handoffs ADD COLUMN context_packet JSON DEFAULT NULL") + conn.commit() + # Add test_command column to projects (KIN-ARCH-008); NULL = auto-detect (KIN-101) projects_cols = {row["name"] for row in conn.execute("PRAGMA table_info(projects)")} if "test_command" not in projects_cols: diff --git a/core/models.py b/core/models.py index d4694ce..b52b9d1 100644 --- a/core/models.py +++ b/core/models.py @@ -42,6 +42,7 @@ _JSON_COLUMNS: frozenset[str] = frozenset({ "dependencies", "steps", "artifacts", "decisions_made", "blockers", + "context_packet", "extra_json", "pending_actions", }) @@ -1191,14 +1192,16 @@ def create_handoff( decisions_made: list | None = None, blockers: list | None = None, status: str = "pending", + context_packet: dict | list | None = None, ) -> dict: """Record a department handoff with artifacts for inter-department context.""" cur = conn.execute( """INSERT INTO department_handoffs - (pipeline_id, task_id, from_department, to_department, artifacts, decisions_made, blockers, status) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", + (pipeline_id, task_id, from_department, to_department, artifacts, decisions_made, blockers, context_packet, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", (pipeline_id, task_id, from_department, to_department, - _json_encode(artifacts), _json_encode(decisions_made), _json_encode(blockers), status), + _json_encode(artifacts), _json_encode(decisions_made), _json_encode(blockers), + _json_encode(context_packet), status), ) conn.commit() row = conn.execute(