Phase 0: Stack Detection (DO NOT SKIP)
This suffixless skill is a router — its job is to detect the workspace stack and dispatch to the matching variant. Run this phase BEFORE Phase 1 below. The dispatch path is non-negotiable; if detection fails, refuse loudly. Do NOT silently fall back to either stack.
Marker checklist (filesystem-only, no network)
Inspect the workspace cwd (the directory the skill was invoked from) plus each depth-1 subdirectory (monorepo support — webapp/package.json, advisor_bot/requirements.txt, etc.). Apply these rules in order. Marker names are the canonical identifiers — keep them verbatim in any refusal output.
Specificity invariant: depth-1 subdir scan widens where markers are looked for, NOT what counts as a marker. web/package.json:name=aura-companion is still matched ONLY by literal name === "aura-companion"; aiogram is still matched ONLY by literal substring / ^aiogram\b line. Directory naming (bot/, webapp/) is NEVER a stack signal on its own.
web/package.json:name=aura-companion— Readweb/package.json(or<subdir>/package.jsonat depth 1). If it exists, parses as JSON, and itsnamefield equals"aura-companion", the Aura stack matches.web/package.json:dependencies.hono— Same file. Ifdependencies.honokey is present, the Aura stack matches.web/server/ws-bridge.ts— If this exact file exists on disk (or<subdir>/server/ws-bridge.tsat depth 1), the Aura stack matches.pyproject.toml:aiogram— Readpyproject.toml(or<subdir>/pyproject.tomlat depth 1). If it exists and contains the literal substringaiogram, the Python stack matches.requirements.txt:^aiogram + bot/— At workspace root: requires BOTHbot/directory ANDrequirements.txtwith a line matching^aiogram\b. At a depth-1 subdir: only requires<subdir>/requirements.txtwith^aiogram\b— the subdir owning its own requirements.txt replaces thebot/co-requirement..council-stack-override— If this file exists, trim its content and treat the value as the explicit stack choice. The closed allow-list is exactlyauraorpython. Anything else (empty, whitespace,Aura,both) → refuse with the override-malformed headline; never silently fall back.
Dispatch rules
.council-stack-overridecontainsauraAND auto-detection found no opposing-only signal → execute the Aura variant of this skill (/council-implement-aura). Override takes precedence; auto-detection markers still surface in the enumeration but do not change the verdict..council-stack-overridecontainspythonAND auto-detection found no opposing-only signal → continue with the Python council body below (the existing Phase 1+ of this file)..council-stack-overrideasserts one stack but auto-detection found ONLY the opposing-stack markers → REFUSE with the override-conflict headline (ask-first). Do NOT silently honour the override against contradictory evidence.- No override + Aura marker matched + no Python marker matched → execute
/council-implement-aura. - No override + Python marker matched + no Aura marker matched → continue with the Python council body below.
- Both Aura and Python markers matched → REFUSE with the ambiguous headline (see refusal templates below).
- Neither matched → REFUSE with the unknown headline.
Refusal templates (pick the headline that matches the failure class)
Always emit the full refusal in plain text. Hard rules:
- Never silently fall back to one of the two stacks on detection failure.
- Never echo raw file content — only filename + parse-status annotation.
- Never use first-person pronouns, apologies, or hedges.
- Keep the refusal compact (target ≤ 18 lines).
Headlines (use the exact wording — these are mirrored by the in-repo verifier web/scripts/detect-stack.ts):
- Unknown stack:
Stack detection: no recognised stack markers at workspace root. - Ambiguous:
Stack detection: both Aura and Python markers present. - Override malformed:
Stack detection: .council-stack-override is malformed. - Override conflict:
Stack detection: .council-stack-override conflicts with auto-detected markers.
Refusal body structure for every class:
<Headline>
Checked for:
- web/package.json:name=aura-companion
- web/package.json:dependencies.hono
- web/server/ws-bridge.ts
- pyproject.toml:aiogram
- requirements.txt:^aiogram + bot/
- .council-stack-override
Found at workspace root:
- <list filenames you actually saw via `ls` — no file content>
(or " (no recognised stack markers)" if the workspace is empty of markers)
To override, run:
/council-implement-aura # if this workspace is the Aura companion
/council-implement # if this workspace is the Python bot (suffixless variant)
After Phase 0 selects a path: if dispatching to the Aura variant, invoke it and stop. If staying in the Python body, proceed to Phase 1 below. If refusing, emit the refusal block exactly as shown and stop.
Phase 4: Emit Council Checkpoint (auto)
Purpose. When this skill runs inside an Aura Companion Council pair, this phase tells the observer half "the orchestrator just finished implementing — here is the file list, please review the implementation." The observer wakes within ~1 second of the checkpoint POST and emits its review to <workspace>/.council/reviews/council-implement-<provider>-observer.md. Without this step the council loop never closes — the observer remains in "Awaiting first checkpoint" forever.
Council pairing is a property of the SESSION, not of the workspace stack. A Python workspace paired in the Aura Companion UI has an observer that needs waking exactly like an Aura workspace does. Do not skip this phase on the grounds that this is not an Aura project.
Skip this phase entirely if any of these are true:
http://localhost:3456/api/sessionsdoes not respond (you are not inside the Aura Companion UI).- No session in the response matches the current workspace + orchestrator role.
- The
POST .../council/checkpointreturns 409 — that response IS the silent-skip signal.
Steps
Probe the API. Run
curl -fsS http://localhost:3456/api/sessions. Non-zero exit or empty → skip the entire phase.Discover the orchestrator session id. Filter the response array for the entry matching ALL of:
state == "connected"cwd == <current workspace cwd>(the cwd you implemented under)sessionGroupRole == "orchestrator"- newest
createdAtif more than one match
Zero matches → silent skip. One match → capture
sessionId(=ORCH_SID) andsessionGroupId(=GROUP_ID).Compute next sequence. List
<workspace>/.council/checkpoints/*.json. Read each file'ssequencefield.next_sequence = max(...) + 1, or0if the directory is empty or absent.Build CheckpointPayload.
{ "schema_version": 1, "checkpoint_id": "council-implement-<seq>-<8-hex-random>", "phase": "council-implement", "sequence": <next_sequence>, "session_group_id": "<GROUP_ID>", "emitted_at": "<UTC ISO 8601 T-form, no fractional seconds>", "artifact_paths": [<workspace-relative paths to every file in the "Ready for Review" list of the implementation log>] }artifact_pathsis the list of modified/created files from the implementation log's "Ready for Review" section — these are exactly the files the observer needs to read to review the implementation. Cap at 50 paths (schema limitMAX_ARTIFACT_PATHS); if you exceeded that count, list the top-50 most-changed and add a NOTE to the log that observer cannot see the rest this cycle.POST. Use the Bash tool:
curl -fsS -X POST \ "http://localhost:3456/api/sessions/$ORCH_SID/council/checkpoint" \ -H "Content-Type: application/json" \ -d "$PAYLOAD"Expected 200 with
{"ok": true, "written": "...", ...}.On non-200:
- 409 ("Session is not part of an active council group"): silent skip, not an error.
- 400 / 403 / 500: surface the raw response body to the user, do not retry.
Do not wait for the observer's review. The 200 response IS the success signal for this phase; the observer's review lands asynchronously in the Aura UI's ObserverPanel.
Multi-task runs: if you emit more than one checkpoint in a single implementation pass, increment sequence per POST and leave ≥1.5s between calls — the watcher's (file, mtimeNs) debounce can otherwise coalesce sub-second neighbours and the observer loses a cycle.