kin: KIN-DOCS-004-backend_dev

This commit is contained in:
Gros Frumos 2026-03-19 20:30:50 +02:00
parent de52526659
commit ae2f0f1c81
8 changed files with 66 additions and 12 deletions

View file

@ -15,6 +15,7 @@ You receive:
**Normal mode** (default): **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 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 2. Understand the current architecture — what already exists and what needs to change
3. Design the solution: data model, interfaces, component interactions 3. Design the solution: data model, interfaces, component interactions

View file

@ -12,6 +12,7 @@ You receive:
## Working Mode ## 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 1. Read all relevant backend files before making any changes
2. Review `PREVIOUS STEP OUTPUT` if it contains an architect spec — follow it precisely 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 3. Implement the feature or fix as described in the task brief

View file

@ -28,6 +28,7 @@ You receive:
- Handoff notes clarity — the next department must be able to start without asking questions - 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 - Previous department handoff — build on their work, don't repeat it
- Sub-pipeline length — keep it SHORT, 1-4 steps maximum - 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:** **Department-specific guidance:**
@ -46,6 +47,7 @@ You receive:
- Each worker brief is self-contained — no "see above" references - Each worker brief is self-contained — no "see above" references
- Artifacts list is complete and specific - Artifacts list is complete and specific
- Handoff notes are actionable for the next department - 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 ## Return Format
@ -72,7 +74,14 @@ Return ONLY valid JSON (no markdown, no explanation):
"schemas": [], "schemas": [],
"notes": "Added feature with full test coverage. All tests pass." "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"
}
} }
``` ```

View file

@ -12,6 +12,7 @@ You receive:
## Working Mode ## 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 1. Read all relevant frontend files before making any changes
2. Review `PREVIOUS STEP OUTPUT` if it contains an architect spec — follow it precisely 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 3. Implement the feature or fix as described in the task brief

View file

@ -1347,14 +1347,29 @@ def _execute_department_head_step(
role = step["role"] role = step["role"]
dept_name = role.replace("_head", "") dept_name = role.replace("_head", "")
# Build initial context for workers: dept head's plan + artifacts # Extract context_packet (KIN-DOCS-004): fail-open if missing
dept_plan_context = json.dumps({ context_packet = parsed.get("context_packet")
"department_head_plan": { 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, "department": dept_name,
"artifacts": parsed.get("artifacts", {}), "artifacts": parsed.get("artifacts", {}),
"handoff_notes": parsed.get("handoff_notes", ""), "handoff_notes": parsed.get("handoff_notes", ""),
}, }
}, ensure_ascii=False) dept_plan_context = json.dumps(dept_plan_context_dict, ensure_ascii=False)
# KIN-084: log sub-pipeline start # KIN-084: log sub-pipeline start
if parent_pipeline_id: if parent_pipeline_id:
@ -1429,6 +1444,7 @@ def _execute_department_head_step(
decisions_made=decisions_made, decisions_made=decisions_made,
blockers=[], blockers=[],
status=handoff_status, status=handoff_status,
context_packet=context_packet,
) )
except Exception: except Exception:
pass # Handoff save errors must never block pipeline pass # Handoff save errors must never block pipeline
@ -1438,6 +1454,7 @@ def _execute_department_head_step(
"from_department": dept_name, "from_department": dept_name,
"handoff_notes": parsed.get("handoff_notes", ""), "handoff_notes": parsed.get("handoff_notes", ""),
"artifacts": parsed.get("artifacts", {}), "artifacts": parsed.get("artifacts", {}),
"context_packet": context_packet,
"sub_pipeline_summary": { "sub_pipeline_summary": {
"steps_completed": sub_result.get("steps_completed", 0), "steps_completed": sub_result.get("steps_completed", 0),
"success": sub_result.get("success", False), "success": sub_result.get("success", False),

View file

@ -181,6 +181,8 @@ specialists:
context_rules: context_rules:
decisions: all decisions: all
modules: all modules: all
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
frontend_head: frontend_head:
name: "Frontend Department Head" name: "Frontend Department Head"
@ -193,6 +195,8 @@ specialists:
context_rules: context_rules:
decisions: all decisions: all
modules: all modules: all
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
qa_head: qa_head:
name: "QA Department Head" name: "QA Department Head"
@ -204,6 +208,8 @@ specialists:
permissions: read_only permissions: read_only
context_rules: context_rules:
decisions: all decisions: all
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
security_head: security_head:
name: "Security Department Head" name: "Security Department Head"
@ -215,6 +221,8 @@ specialists:
permissions: read_only permissions: read_only
context_rules: context_rules:
decisions_category: security decisions_category: security
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
infra_head: infra_head:
name: "Infrastructure Department Head" name: "Infrastructure Department Head"
@ -226,6 +234,8 @@ specialists:
permissions: read_only permissions: read_only
context_rules: context_rules:
decisions: all decisions: all
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
knowledge_synthesizer: knowledge_synthesizer:
name: "Knowledge Synthesizer" name: "Knowledge Synthesizer"
@ -252,6 +262,8 @@ specialists:
permissions: read_only permissions: read_only
context_rules: context_rules:
decisions: all decisions: all
output_schema:
context_packet: "{ architecture_notes: string, key_files: array, constraints: array, unknowns: array, handoff_for: string }"
marketing_head: marketing_head:
name: "Marketing Department Head" name: "Marketing Department Head"
@ -264,6 +276,8 @@ specialists:
context_rules: context_rules:
decisions: all decisions: all
modules: 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 — PM uses these when routing complex cross-domain tasks to department heads
departments: departments:

View file

@ -165,6 +165,7 @@ CREATE TABLE IF NOT EXISTS department_handoffs (
artifacts JSON, artifacts JSON,
decisions_made JSON, decisions_made JSON,
blockers JSON, blockers JSON,
context_packet JSON DEFAULT NULL,
status TEXT DEFAULT 'pending', status TEXT DEFAULT 'pending',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP created_at DATETIME DEFAULT CURRENT_TIMESTAMP
); );
@ -703,6 +704,13 @@ def _migrate(conn: sqlite3.Connection):
""") """)
conn.commit() 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) # 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)")} projects_cols = {row["name"] for row in conn.execute("PRAGMA table_info(projects)")}
if "test_command" not in projects_cols: if "test_command" not in projects_cols:

View file

@ -42,6 +42,7 @@ _JSON_COLUMNS: frozenset[str] = frozenset({
"dependencies", "dependencies",
"steps", "steps",
"artifacts", "decisions_made", "blockers", "artifacts", "decisions_made", "blockers",
"context_packet",
"extra_json", "extra_json",
"pending_actions", "pending_actions",
}) })
@ -1191,14 +1192,16 @@ def create_handoff(
decisions_made: list | None = None, decisions_made: list | None = None,
blockers: list | None = None, blockers: list | None = None,
status: str = "pending", status: str = "pending",
context_packet: dict | list | None = None,
) -> dict: ) -> dict:
"""Record a department handoff with artifacts for inter-department context.""" """Record a department handoff with artifacts for inter-department context."""
cur = conn.execute( cur = conn.execute(
"""INSERT INTO department_handoffs """INSERT INTO department_handoffs
(pipeline_id, task_id, from_department, to_department, artifacts, decisions_made, blockers, status) (pipeline_id, task_id, from_department, to_department, artifacts, decisions_made, blockers, context_packet, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(pipeline_id, task_id, from_department, to_department, (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() conn.commit()
row = conn.execute( row = conn.execute(