"Deterministic generate/check producers for documentation, API contracts, decisions, and OpenSpec projections over the ri-06 ProducerResult contract"
Resources
2Install
npx skillscat add jankneumann/agentic-coding-tools/project-context-refresh Install via the SkillsCat registry.
Project Context Refresh
Shared registry of deterministic context producers. Each producer regenerates
one class of derived project context and, in check mode, reports precise drift
without touching the checkout — never using file modification times. Every
producer returns the canonical ri-06 ProducerResult
(project-context-runtime), so the refresh orchestrator (ri-07) records results
with no translation.
This is an infrastructure skill — not user-invocable. Add scripts/ tosys.path and import the bare module names, or drive it through scripts/cli.py.
Producers
| Producer id | Canonical owner | Managed output |
|---|---|---|
documentation.inventory |
this skill (absorbs add-update-documentation-skill) |
docs/architecture-analysis/skills-inventory.md |
api.contracts |
openspec/contracts/ schemas |
docs/architecture-analysis/contracts-inventory.md |
decisions.timeline |
explore-feature/archive_index.py (make decisions) |
docs/decisions/ |
openspec.projection |
cleanup-feature / openspec archive |
openspec/specs/ (projection only — never written) |
Modes
generate— write a producer's declared managed outputs (byte-stable for a
fixed revision, inputs, and producer version).check— render in memory / a tempdir and byte-compare; never writes.
Drift is reported asdegradedwith a failed validation, remediation, and acustomfallback stating no write occurred. A clean check isfresh.
openspec.projection is projection-only: canonical spec merges are sync-point
mutations owned by cleanup-feature, so both modes are read-only.
Orchestration (ri-07)
scripts/orchestrator.py drives all configured producers into one durable
ri-06 operation per (repository, revision) and emits the manifest:
generate(...)— reuse/create the canonical operation, run every configured
producer, record each result before attempting the degradable semantic
index (ri-02), finalizesucceeded/degraded/failed, then write and record
the manifest. A repeat run at the same revision reuses asucceededoperation
verbatim (no re-attempt, no repository diff).check(...)— fully read-only drift assessment (no store or working-tree
writes).
The semantic index is the one degradable producer: unavailable or errored →
a non-succeeded SemanticIndexReference with an exact-search fallback (never
fatal, deterministic output preserved). Architecture (ri-04) is collected via its
canonical owner; the manifest is written to the gitignored.git-context/context-refresh-manifest.json so reruns never dirty the tree.
The proposal's capability producer has no canonical owner and is not
configured — tracked as an ri-07 follow-up (coordinator issuedced1d51, candidate change add-capability-context-producer). Once such a
producer registers in registry.py, the orchestrator picks it up automatically.
CLI
From the repository root, the Makefile wraps the registry and the orchestrator:
make context-refresh # generate every producer's managed output (ri-05)
make context-refresh-check # read-only per-producer drift check (0 fresh · 2 drift · 1 failed)
make refresh-project-context # orchestrate all producers + emit the manifest (ri-07)
make refresh-project-context-check # read-only orchestrated drift check (0/2/1)The underlying entry point is cli.py in this skill's resolved scripts/
directory, with subcommands list, generate <producer_id>,check <producer_id>, generate-all, check-all, refresh [--producer ID],
and refresh-check [--producer ID]. Resolve the loaded skill directory first
rather than hardcoding an install path.
Sync-point flags (ri-11)
refresh carries two opt-in flags for the main-convergence sync point. Both
default to off, so every existing invocation behaves exactly as before, andrefresh-check accepts neither — it writes nothing and never indexes.
| Flag | Default | Effect |
|---|---|---|
--sync-point |
off | Authorizes the mutation from the shared checkout, reaching the checkout policy's approved_sync_point branch. |
--defer-semantic-index |
off | Skips the inline index attempt and records the index as pending with an exact-search fallback. |
--sync-point is a caller decision and is never inferred from the
environment: an environment sniff would re-open shared-checkout writes for every
skill that happens to run on main, which is the property the guard exists to
protect. It authorizes the checkout classification only — the caller must still
enforce its own clean-tree and active-agent guards, and its own pre-push
compare-and-swap.
--defer-semantic-index exists because a sync point refreshes a revision that is
never main's final state: the convergence commit follows it, so an inline index
would be stale on arrival and a correct system would then index a second time.
The caller enqueues exactly one index for the final pushed revision instead.
Deferral only ever weakens the recorded claim — pending is not a currency
claim, so a deferred run degrades (exit 2) rather than reporting success, and
deterministic producer output is byte-identical to a non-deferred run.
# What the convergence driver runs, on main, after the merges have landed.
python3 <agent-skills-dir>/project-context-refresh/scripts/cli.py \
refresh --sync-point --defer-semantic-index--revision must name the revision that is actually checked out: every
producer reads the live working tree, so accepting another SHA would persist
artifacts under a revision they did not come from. Use a worktree at the target
revision instead.
Configuration
All optional — with none of it set, refresh still runs every deterministic
producer and degrades the semantic index to not-configured with anexact-search fallback.
| Variable | Purpose |
|---|---|
PROJECT_CONTEXT_REPO_ID |
Repository identity shared with refresh-architecture's provenance.repository_id. Both must agree or one clone splits across two operation ids. |
POSTGRES_DSN |
Semantic-index database. Absent → indexing is unconfigured. |
PROJECT_CONTEXT_EMBEDDING_MODEL |
Embedding model id. Required to enable indexing. |
PROJECT_CONTEXT_EMBEDDING_DIMENSION |
Embedding dimension. Required to enable indexing. |
PROJECT_CONTEXT_EMBEDDING_PROVIDER |
local (default) or openai_compatible. |
PROJECT_CONTEXT_EMBEDDING_CREDENTIAL_REF |
Credential reference (env:NAME / vault:path) for a remote provider. |
PROJECT_CONTEXT_INDEX_TIMEOUT |
Seconds allowed for one indexing run (default 1800). |
The embedding contract is complete-or-absent: a DSN without a model and a
dimension is treated as unconfigured rather than dispatched. Indexing runs theindex_repo console script from packages/code-search as a subprocess — it
pins asyncpg<0.31 against the coordinator's >=0.31, so it cannot be imported
in-process. Every non-ready outcome degrades the refresh; it never fails it.
What it owns / does not own
- Owns: producer registration, fail-closed invocation, generate/check protocol,
the domain adapters, and (ri-07) cross-producer orchestration — driving every
configured producer into one ri-06 operation and emitting the aggregate
manifest. - Does not own: the result/manifest/operation models or durable storage
(ri-06project-context-runtime); CI/merge drift gates (ri-10/ri-11); the
architecture analysis (refresh-architecture, ri-04) or semantic indexing
(ri-01…ri-03) themselves — orchestration collects their results but each
remains owned and regenerated by its canonical owner. - Does not keep architecture artifacts fresh for anyone else. They are a
regenerable local analysis cache whose freshness is a property of the checkout
that last regenerated them, so an orchestration run here cannot observe — let
alone guarantee — the freshness of the checkout a reader is about to read from.
The reader ensures instead: the six consumer skills callrefresh-architecture/scripts/run_architecture.py --ensureat the top of their
artifact-reading step, and that call writes nothing when the check is fresh.
This skill'sarchitectureproducer reports; it does not ensure on their
behalf, and its result is informational drift in the gate. scripts/checkpoint.pyis the same posture, deliberately. It records
architecture freshness and the merge-base delta as findings and never
invokes--ensure: a branch-local report of what a branch invalidated has to
be reproducible, and here regenerating would also rewrite tracked
committed-tier artifacts on any stale branch.
Tests
skills/tests/project-context-refresh/ — run withskills/.venv/bin/python -m pytest skills/tests/project-context-refresh -q.