| name | init-project |
| description | Use when: kullanıcı "yeni proje", "init project", "bootstrap project",
"{slug} kur", "{slug} ekle", "workspace bootstrap", "yeni proje aç"
der ya da /pseo-init çağırır.
Also use when: pilot project workspace iskelet eksik (projects/{slug}/
yok ya da master.xlsx ve project.config.json mevcut değil); workspace-
staging'de yeni bir slug ilk kez kuruluyor; idempotent re-bootstrap
isteniyor (var olan master.xlsx KORUNUR, sadece config refresh).
Do not use when: project zaten kurulmuş ve sadece veri ingest edilecek
— sf-import, quick-wins veya başka discovery/ingestion skill kullan;
mevcut master.xlsx'i schema-uyumsuz şekilde yeniden yaratmak gerekiyor
— bu yıkıcı, init-project ASLA mevcut master.xlsx'e dokunmaz (F1
workbook policy + idempotency invariant).
|
| version | 1.0 |
| status | active |
| category | meta |
| inputs | {"project_slug":{"type":"string","required":true,"description":"Lowercase kebab-case (e.g. 'my-project'). Matches projects/{slug}/."},"domain":{"type":"string","required":true,"description":"Site URL (https://...). Used as default gsc.site_url."},"gsc_site_url":{"type":"string","required":false,"description":"Override GSC property URL when distinct from domain. If set, mcp__gsc__list_sites is invoked to verify ownership."},"market":{"type":"string","required":false,"default":"TR","description":"ISO 3166-1 alpha-2 country code."},"locale":{"type":"string","required":false,"default":"tr-TR","description":"IETF BCP 47 content locale."},"profile":{"type":"array","required":false,"default":["local-service"],"description":"Composable project profiles (project-config.profiles)."}} |
| outputs | ["projects/{slug}/project.config.json","projects/{slug}/master.xlsx","projects/{slug}/_state/events.jsonl","projects/{slug}/_state/workflows/{run_id}.json","shared/portfolio.json"] |
| consumes | ["templates/master-excel.xlsx"] |
| produces | ["sf-import","quick-wins"] |
| triggers | {"manual":["/pseo-init"],"natural_language":"\"yeni proje\", \"init project\", \"bootstrap project\", \"{slug} kur\",\n\"{slug} ekle\", \"workspace bootstrap\", \"yeni proje aç\"\n","hooks":[],"scheduled":[]} |
| mcp_tools | {"optional":["mcp__gsc__list_sites"]} |
| budget | {"uses_paid_mcp":false,"estimated_credits":0} |
| autonomy | {"confidence":"HIGH","requires_approval":true,"safe_auto_execute":false} |
init-project — meta skill (Phase 5 Wave 2, project bootstrap)
10-step protocol that scaffolds a brand-new project pack into the
workspace. Idempotent by design: re-running against an existing project
NEVER mutates master.xlsx, only refreshes project.config.json and
appends a portfolio.json entry if missing. The skill is the entry
point for every new domain; downstream Phase 6+ ingestion skills
(sf-import, quick-wins, pillar-builder) all assume init-project ran
first.
Inputs (frontmatter contract)
| Name | Type | Default | Notes |
|---|
project_slug | string | — | Required. Lowercase kebab-case; matches projects/{slug}/ dir. |
domain | string | — | Required. Project root URL (https://...). Default gsc.site_url. |
gsc_site_url | string | (= domain) | Override when GSC property URL differs from domain. |
market | string | TR | ISO 3166-1 alpha-2. |
locale | string | tr-TR | IETF BCP 47. |
profile | array | ["local-service"] | Composable project profiles (project-config.profiles enum). |
Outputs (artifacts produced)
projects/{slug}/project.config.json — schema-valid project pack
config (project-config.schema.json v1.0).
projects/{slug}/master.xlsx — schema-shaped workbook copied from
templates/master-excel.xlsx (lowercase logical name; F1 workbook
policy). NEVER overwritten on re-run.
projects/{slug}/_state/events.jsonl — provenance event for
operation=project_excel (kind=tool_computed,
workflow_action=done).
projects/{slug}/_state/workflows/{run_id}.json — workflow run state
(workflow-run.schema.json).
shared/portfolio.json — append-only portfolio registry (slug,
domain, market, created_at).
10-Step Body Protocol
Step names match steps[*].name passed to workflow_runner.create_run.
Names are stable identifiers across runs.
Step 1 — Validate inputs
project_slug must match ^[a-z][a-z0-9-]*$ (project-config.schema
pattern). domain must be a non-empty URI. Empty / whitespace / wrong
case → DURUR (do not patch, flag the manager).
Step 2 — create_run
from scripts.state import workflow_runner
handle = workflow_runner.create_run(
skill="init-project",
project_slug=project_slug,
steps=[
{"name": "verify_gsc"},
{"name": "bootstrap_config"},
{"name": "copy_master_xlsx"},
{"name": "register_portfolio"},
{"name": "request_approval"},
{"name": "emit_provenance"},
],
)
The state file lives at
projects/{slug}/_state/workflows/{run_id}.json (ADR-021).
Step 3 — verify_gsc (optional)
Skipped when gsc_site_url not supplied (or domain mode). When set,
call mcp__gsc__list_sites and assert the URL appears with at least
siteOwner / siteFullUser / siteRestrictedUser permission. If the
property is absent or auth fails, the step's output_ref records the
miss but the run continues — verification is advisory at bootstrap
time (the user may have just registered the property and propagation
is async). DURUR only if the MCP returns an auth/network error
distinct from "not present".
Step 4 — bootstrap_config
Call the existing CLI module via subprocess (no in-process import; the
CLI's --force semantics are the contract):
python3 scripts/state/bootstrap_project.py \
--project {slug} \
--domain {domain} \
--market {market} --locale {locale} \
[--gsc-site-url {gsc_site_url}] \
[--profile {profile_i} ...] \
--out {workspace_root}/projects/{slug}/project.config.json \
--force
Validates the resulting JSON against schemas/project-config.schema.json
(Draft7) before returning. Idempotent — --force overwrites stale
config, but the file content is fully derived from inputs.
Schema version default (v1.8 Phase 1): bootstrap_project.py emits
schema_version: "1.5" by default — the SF MCP additive block
(sf.mcp.*) is included in every freshly-bootstrapped project per
D-SF-12 + D-SF-18 path parameterization. New project packs are
schema-1.5 native; no migration is invoked.
Step 4.5 — cascade_migration_0005 (safety net for legacy 1.4 docs)
For projects re-bootstrapped from a pre-Phase-1 (schema 1.4) snapshot,
init-project supports an explicit operator opt-in:
python3 scripts/migrations/migration_0005_project_config_1_4_to_1_5.py \
--in projects/{slug}/project.config.json
Without the flag, init-project trusts bootstrap_project.py as the
authority (current default emits 1.5). With the flag, the cascade
ensures schema 1.5 is final-state irrespective of bootstrap CLI
version. The flag is operator-controlled because cascading a
migration touches the bytes a Manager may have committed; explicit
opt-in keeps the audit trail deliberate.
Step 5 — copy_master_xlsx (IDEMPOTENT, F1 policy)
target = workspace_root / "projects" / project_slug / "master.xlsx"
if target.exists():
output_ref = f"master.xlsx#sha256={sha256_of(target)}#preserved"
else:
template = repo_root / "templates" / "master-excel.xlsx"
if not template.exists():
raise BootstrapError("templates/master-excel.xlsx missing")
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(template, target)
output_ref = f"master.xlsx#sha256={sha256_of(target)}#created"
The legacy {Slug}_MASTER.xlsx workbook (Turkish display name) is
NEVER touched by this skill. F1 (workbook policy): master.xlsx
(lowercase, schema-shaped logical sheet names) is the canonical
artifact for all new ingest flows; the display-name workbook is
read-only legacy data.
Step 6 — register_portfolio
Append the project to {workspace_root}/shared/portfolio.json (creates
the file + parent dir if missing). Append-only: existing entries are
preserved; only the new slug entry is added.
The append is delegated to scripts/state/portfolio_writer.register_project,
which runs the read-modify-write under a single fcntl.flock(LOCK_EX) on the
file so two parallel init-project runs cannot lose an update. The on-disk
shape is unchanged — {"schema_version": "1.0", "projects": [{slug, domain, market, created_at}, …]}, indent=2, trailing newline, ensure_ascii=False —
and re-registering an existing slug is an idempotent no-op (dedup on slug).
from scripts.state.portfolio_writer import register_project
register_project(
workspace_root,
project_slug,
domain,
market,
created_at=utc_iso_z(),
)
Step 7 — request_approval
workflow_runner.request_approval(
handle.run_id, project_slug=project_slug,
approver="user",
subject=(
f"Config review: project_slug={project_slug}, "
f"domain={domain}, gsc_site_url={gsc_site_url or domain}. "
"Onaylıyor musun?"
),
step_index=4,
)
Skill EXITS at this point (status=awaiting_approval). The user
replies in a fresh session; resume below.
Step 8 — Resume (approve → continue)
workflow_runner.approve(
handle.run_id, project_slug=project_slug, approver="user",
)
Step 9 — emit_provenance
from scripts.state import events_writer
events_writer.append_provenance(
project_id=project_slug,
source={"kind": "tool_computed"},
operation="project_excel",
target_excel_sheet=None,
notes="init-project bootstrap completed",
)
source.kind=tool_computed reflects that bootstrap is not an external
ingest; it is an in-process scaffolding action. target_excel_sheet
is null because master.xlsx is COPIED from a template, not written
sheet-by-sheet.
Step 10 — complete
workflow_runner.complete(
handle.run_id, project_slug=project_slug,
outputs={
"config_path": "projects/{slug}/project.config.json",
"master_xlsx_path":"projects/{slug}/master.xlsx",
"portfolio_entry": "shared/portfolio.json#projects[{slug}]",
},
)
All outputs.* values are STRING-TYPED artifact paths (workflow-run
schema constraint; F5 rule). Numeric counts go in events.jsonl, not
in workflow_run.outputs.
Planned Cascade — brand-onboarding (Phase 3 G-AI-05; Phase-14 deferral)
PLANNED (Phase 14) — not yet wired. The cascade auto-runner and the
cascade: brand-onboarding event mechanism described here do NOT exist
in the current codebase: no skill auto-runner consumes a cascade event,
and init-project reaches terminal status=done at Step 10 with no
downstream gate. This section documents the INTENDED Phase-14 behavior,
mirroring brand-onboarding's own staging→canonical deferral framing.
Once the cascade auto-runner exists (Phase 14), after Step 10 emits
complete init-project will emit a cascade: brand-onboarding event to
_state/events.jsonl; the auto-runner will pick it up and invoke
brand-onboarding with the freshly scaffolded project slug, whose 3-stage
bank-seed pipeline (Stages A + B + C) populates
content_settings.experience_database and
content_settings.original_research_database.
Planned completion gate (Phase 14, once the cascade is live): init
will not be considered "complete" until brand-onboarding Stage C writes
successfully —
- YMYL profile: ≥3 approved experience entries seeded (DURUR #9
BANK-SEED-PROFILE-MIN-NOT-MET).
- Non-YMYL profile: ≥1 approved experience entry OR operator
explicit-skip via decisions map.
Today (pre-Phase-14): Step 10's complete IS terminal — there is no
cascade emission and no completion gate. Bank seeding is a separate,
operator-initiated brand-onboarding run. The paired test
(tests/skills/test_init_project.py) asserts this terminal-done
contract.
Why this cascade is planned: the May 2026 Core Update emphasizes
"original, helpful, people-first content" and penalizes
"automated, ad-bloated, repetitive content". Empty banks (the
pre-Phase-3 default state of all 9 projects) trivially pass
R-105 / R-114 / R-119 — the rules require entries to be USED but
existing skills produce empty-bank content silently. Bank seed
is therefore prerequisite to E-E-A-T "Experience" depth signal.
Idempotency invariant
Re-running init-project against an existing project MUST satisfy:
master.xlsx SHA-256 unchanged (file bytes preserved verbatim).
project.config.json is regenerated from current inputs (so config
evolution is supported), but the Excel artifact is sacred.
shared/portfolio.json gains AT MOST one new entry; duplicate slug
inserts are no-ops.
- A new
_state/workflows/{run_id}.json is created (each invocation
gets its own audit trail; old runs are not amended).
- A new provenance event is appended (append-only; never modified).
DURUR conditions (6)
Stop and flag the manager — do not patch, do not fall back.
project_slug invalid (fails ^[a-z][a-z0-9-]*$, contains spaces
or special chars).
bootstrap_project.py CLI exits non-zero (validation/IO error).
templates/master-excel.xlsx missing — cannot bootstrap workbook.
workflow_runner.create_run fails schema validation
(workflow-run.schema.json).
PSEO_WORKSPACE_ROOT env var unset AND no explicit workspace_root
passed.
- Idempotency violation: existing
master.xlsx would be mutated
(caller passed --force or template differs and writer attempted
overwrite). The skill must abort before touching the file.
Cross-references
- Schemas:
schemas/skill-frontmatter.schema.json (this file's
frontmatter contract), schemas/project-config.schema.json (the
config artifact), schemas/master-excel.schema.json (the workbook
shape — referenced by templates/master-excel.xlsx),
schemas/workflow-run.schema.json, schemas/events.schema.json,
schemas/portfolio-config.schema.json (Phase 14+ richer portfolio).
- Cross-modules:
scripts/state/bootstrap_project.py (CLI),
scripts/state/workflow_runner.py (state machine),
scripts/state/events_writer.py (provenance),
scripts/state/portfolio_writer.py (portfolio registry, flock-guarded),
scripts/excel/bootstrap_excel.py (template generator).
- Template:
templates/master-excel.xlsx.
- Tests:
tests/skills/test_init_project.py (5 cases; idempotency,
schema validity, GSC verify, provenance, portfolio registry).