Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/decodex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,5 @@ fn install_panic_hook() {
}));
}

#[cfg(test)] mod plugin_surface_tests;
#[cfg(test)] mod test_support;
92 changes: 92 additions & 0 deletions apps/decodex/src/plugin_surface_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use serde_json::Value;

const PLUGIN_JSON: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../plugins/decodex/.codex-plugin/plugin.json"
));
const DECODEX_SKILL: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../plugins/decodex/skills/decodex/SKILL.md"
));
const PLANNING_SKILL: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../plugins/decodex/skills/planning/SKILL.md"
));
const AUTOMATION_SKILL: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../plugins/decodex/skills/automation/SKILL.md"
));
const LABELS_SKILL: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../plugins/decodex/skills/labels/SKILL.md"
));

#[test]
fn packaged_plugin_manifest_routes_natural_language_research_to_decodex() {
let manifest: Value = serde_json::from_str(PLUGIN_JSON).expect("plugin manifest should parse");
let interface = manifest
.get("interface")
.and_then(Value::as_object)
.expect("plugin interface should be an object");
let long_description = interface
.get("longDescription")
.and_then(Value::as_str)
.expect("longDescription should be a string");
let default_prompts = interface
.get("defaultPrompt")
.and_then(Value::as_array)
.expect("defaultPrompt should be an array")
.iter()
.filter_map(Value::as_str)
.collect::<Vec<_>>()
.join("\n");

assert_contains(long_description, "natural-language-first");
assert_contains(long_description, "\"research X\"");
assert_contains(long_description, "latent Decision Contracts");
assert_contains(long_description, "\"arrange this\"");
assert_contains(long_description, "\"推进\"");
assert_contains(long_description, "queues only ready nodes");
assert_contains(long_description, "graph, DAG, goal, and queue mechanics backstage");
assert_contains(&default_prompts, "Research how Decodex should handle this.");
assert_contains(
&default_prompts,
"Arrange the accepted Decodex research contract into executable issues.",
);
}

#[test]
fn packaged_skills_preserve_research_promotion_and_queue_boundaries() {
assert_contains(DECODEX_SKILL, "## Natural-Language Research Routing");
assert_contains(DECODEX_SKILL, "`research X`");
assert_contains(DECODEX_SKILL, "latent Decision Contract");
assert_contains(DECODEX_SKILL, "`arrange this`");
assert_contains(DECODEX_SKILL, "`推进`");
assert_contains(DECODEX_SKILL, "Do not queue work");
assert_contains(DECODEX_SKILL, "Queue labels are an intake signal");
assert_contains(PLANNING_SKILL, "accepted Decision Contract");
assert_contains(PLANNING_SKILL, "Do not use planning to turn a plain `research X`");
assert_contains(PLANNING_SKILL, "queue only mapped issues");
assert_contains(PLANNING_SKILL, "Promotion is a separate authority boundary");
assert_contains(AUTOMATION_SKILL, "Automation starts only after execution authority exists");
assert_contains_normalized(
AUTOMATION_SKILL,
"latent research must not dispatch retained lanes",
);
assert_contains(AUTOMATION_SKILL, "accepted/promoted Decision Contract");
assert_contains(AUTOMATION_SKILL, "blocked, stale, paused, active, terminal");
assert_contains_normalized(LABELS_SKILL, "not the user-facing research/design workflow");
assert_contains(LABELS_SKILL, "accepted/promoted Decision");
assert_contains(LABELS_SKILL, "Do not ask ordinary users to apply queue labels");
}

fn assert_contains(haystack: &str, needle: &str) {
assert!(haystack.contains(needle), "expected packaged plugin content to contain `{needle}`");
}

fn assert_contains_normalized(haystack: &str, needle: &str) {
let normalized_haystack = haystack.split_whitespace().collect::<Vec<_>>().join(" ");
let normalized_needle = needle.split_whitespace().collect::<Vec<_>>().join(" ");

assert_contains(&normalized_haystack, &normalized_needle);
}
8 changes: 5 additions & 3 deletions plugins/decodex/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "decodex",
"version": "0.1.0",
"description": "Agent-facing workflows for planning, using, and operating Decodex in manual CLI and runtime-owned automation modes.",
"description": "Agent-facing workflows for Decodex research/design intake, planning, manual CLI use, and runtime-owned automation.",
"author": {
"name": "hack-ink",
"email": "hi@hack.ink",
Expand All @@ -20,8 +20,8 @@
"skills": "./skills/",
"interface": {
"displayName": "Decodex",
"shortDescription": "Use Decodex manually or as an automated retained-lane control plane.",
"longDescription": "Routes Codex agents through Decodex's planning and usage surfaces: Decodex-friendly issue splitting, queue shaping, and concurrency; manual CLI development workflows such as commit, land, status, project registration, account selection, and dry runs; and runtime-owned automation workflows such as serve, registered project contracts, Linear labels, tracker tools, review handoff, landing, closeout, and operator status. The plugin keeps Decodex-specific instructions with the Decodex runtime instead of duplicating them in generic playbooks.",
"shortDescription": "Use Decodex for research/design intake, planning, manual CLI work, or retained-lane automation.",
"longDescription": "Routes Codex agents through Decodex's natural-language-first surfaces: ordinary requests like \"research X\" enter Decodex-native research/design and produce latent Decision Contracts rather than automatic implementation; later follow-ups such as \"arrange this\", \"push this forward\", \"推进\", or \"做\" can promote accepted contracts into planning and issue shaping; planning splits accepted work into normal Linear issues and queues only ready nodes through registered project policy; manual CLI workflows such as commit, land, status, project registration, account selection, and dry runs stay explicit; and runtime-owned automation workflows such as serve, registered project contracts, Linear labels, tracker tools, review handoff, landing, closeout, and operator status keep their narrow authority. The plugin keeps graph, DAG, goal, and queue mechanics backstage for ordinary users while preserving Decodex-specific instructions with the runtime instead of duplicating them in generic playbooks.",
"developerName": "hack-ink",
"category": "Development",
"capabilities": [
Expand All @@ -32,6 +32,8 @@
"privacyPolicyURL": "https://github.com/hack-ink/decodex",
"termsOfServiceURL": "https://github.com/hack-ink/decodex",
"defaultPrompt": [
"Research how Decodex should handle this.",
"Arrange the accepted Decodex research contract into executable issues.",
"Use Decodex to inspect this lane.",
"Split this feature into Decodex-friendly independent issues.",
"Run the Decodex automation preflight.",
Expand Down
22 changes: 22 additions & 0 deletions plugins/decodex/skills/automation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ description: "Use for Decodex runtime-owned automation: registered projects, `se

Operate Decodex as the retained-lane control plane for automatic development.

Automation starts only after execution authority exists. A natural-language
`research X` request can create a latent Decision Contract, but latent research must
not dispatch retained lanes, set Codex goals, mutate tracker state, or apply queue
labels. A later promotion request such as `arrange this`, `push this forward`, `推进`,
or `做` may feed planning and ready-node queueing only after the accepted contract is
clear.

## Governing Surfaces

- `project.toml` under `~/.codex/decodex/projects/<service-id>/` owns repo paths,
Expand Down Expand Up @@ -87,6 +94,21 @@ on the next 15-second control-plane tick and still respects tracker rate-limit b
silently retry.
- Use the `labels` skill before adding, clearing, or interpreting these labels.

For research-to-execution work, automatic intake also requires:

- an accepted/promoted Decision Contract or an equivalent explicit human instruction
that grants execution authority
- normal Linear issues with natural-language briefs, acceptance, dependencies, and
validation expectations
- readiness under the registered `WORKFLOW.md`, including startable state, dependency
policy, opt-out labels, active ownership, terminal-state rules, and local capacity
- queue labels only on ready issues; blocked, stale, paused, active, terminal, or
unmapped internal nodes remain unqueued

Do not expose graph ids, DAG edge editing, hidden goal ids, or queue-label mechanics as
the ordinary user workflow. Operators may inspect status, but automation policy owns
the backstage readiness and intake mechanics.

## Lane Completion

The coding agent must leave exactly one terminal path for the leased issue:
Expand Down
43 changes: 38 additions & 5 deletions plugins/decodex/skills/decodex/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,47 @@ description: Use as the conductor for Decodex work whenever the user asks to use
## Goal

Route agent work through the right Decodex surface without duplicating the runtime
specs. Decodex has two supported use modes:
specs. Decodex has these supported use modes:

- Research/design mode: natural-language requests such as `research X` enter the
Decodex-native Research/Decision path. The result is a latent Decision Contract,
not execution authority.
- Manual CLI mode: a human is driving local development, commits, PR preparation,
landing, status inspection, project registration, account selection, or dry-run
checks.
- Automation mode: Decodex owns retained-lane execution through registered project
contracts, `serve`, `run`, tracker labels, issue-scoped tools, review handoff,
landing, closeout, and operator status.
- Planning support: agents shape Decodex-friendly issue sets, queue strategy,
dependency boundaries, and concurrency before retained-lane automation starts.
- Research/design mode: Decodex compiles ambiguous planning intent into local
Decision Contracts with `decodex research compile`, then records acceptance with
`decodex research promote` before any execution authority exists.
dependency boundaries, and concurrency after a human request or accepted/promoted
Decision Contract needs executable issue shaping.

## Natural-Language Research Routing

Keep the everyday user surface conversational. Do not require the user to mention
Research Lanes, Decision Lanes, DAGs, Execution Programs, queue labels, or Codex goal
commands.

Route by intent:

1. If the user says `research X`, asks for a design investigation, or asks Decodex to
figure out what should be done before implementation, treat it as research/design
intake. Produce or update a latent Decision Contract with evidence, assumptions,
options, objections, non-goals, acceptance criteria, stop conditions, readiness,
and open decisions. Do not queue work, create execution authority, mutate tracker
state, or start implementation from the research request alone.
2. If the user later says `arrange this`, `push this forward`, `推进`, `做`, or an
equivalent follow-up that clearly accepts or promotes the prior contract, treat that
as promotion to execution authority. Preserve the accepted contract boundary; if
direction is still missing or contradictory, ask for the missing decision instead
of starting work.
3. After promotion, use `planning` to convert the accepted contract into normal
Linear issues with clear natural-language briefs, dependencies, acceptance, and
validation. Keep Execution Program and graph mechanics as internal readiness state.
4. Use `labels` and `automation` only for nodes/issues that are ready under the
registered project policy. Queue labels are an intake signal for retained lanes,
not a shortcut around blockers, opt-outs, terminal states, active leases, or
missing briefing.

## First Steps

Expand All @@ -38,6 +66,11 @@ specs. Decodex has two supported use modes:
- `land` for `decodex land`.
- `labels` for Decodex Linear labels.

Use explicit `decodex research compile` and `decodex research promote` commands only
when the operator is asking for the manual CLI surface. Ordinary conversational
research/promotion should still follow the same latent-then-promoted authority
boundary without making the user learn the commands.

## Authority Split

- Runtime behavior belongs to `apps/decodex/src/` and `docs/spec/`.
Expand Down
12 changes: 11 additions & 1 deletion plugins/decodex/skills/labels/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ description: Use when applying, clearing, or interpreting Decodex Linear labels

Handle Decodex-related Linear labels without changing runtime ownership by accident.

Labels are retained-lane intake and ownership signals. They are not the user-facing
research/design workflow and they do not promote latent Decision Contracts by
themselves.

## Label Catalog

| Label | Meaning |
Expand Down Expand Up @@ -53,7 +57,10 @@ If a project-scoped command supplies `--config <project-dir>`, read that project
checks, dependency checks, or active-lease checks.
4. Ensure `decodex:manual-only` is absent.
5. Ensure any prior blocker behind `decodex:needs-attention` is resolved.
6. Add `decodex:queued:<service-id>`.
6. For research-to-execution work, ensure the source is an accepted/promoted Decision
Contract or an equivalent explicit human execution instruction, not only a plain
`research X` result or latent contract.
7. Add `decodex:queued:<service-id>`.

## Pause or Opt Out

Expand All @@ -73,5 +80,8 @@ If a project-scoped command supplies `--config <project-dir>`, read that project
- Do not use `decodex:active:<service-id>` to mean "please start work".
- Do not clear `decodex:needs-attention` just to silence a failed lane.
- Do not add a service-scoped label for the wrong registered service.
- Do not ask ordinary users to apply queue labels, mention DAG/goal mechanics, or
manage internal readiness state just to move from research to execution; route that
through promotion, planning, and automation policy.
- Use `land` when the task is really about human-driven PR landing.
- Use `commit` when the task is really about human-driven commit creation.
25 changes: 25 additions & 0 deletions plugins/decodex/skills/planning/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Use this before queueing a broad feature, migration, or cleanup effort into Deco
For durable issue text, pair this skill with the delivery plugin's `split` or `issue`
skill, then use the `labels` skill when applying Decodex intake labels.

Use this after a natural-language promotion follow-up such as `arrange this`,
`push this forward`, `推进`, or `做` when the accepted Decision Contract is ready to
become executable work. Do not use planning to turn a plain `research X` request or
latent contract into queued implementation.

## Read First

- The registered project `project.toml` for `service_id`, repo path, worktree root,
Expand Down Expand Up @@ -45,6 +50,20 @@ Each issue should have:
Do not make the description only a machine-readable fenced block. Generic normal
dispatch requires a usable briefing surface.

When issues come from an accepted/promoted Decision Contract, each issue should also
carry:

- objective lineage back to the accepted contract in plain language
- the accepted scope, non-goals, constraints, objections, and stop conditions relevant
to that issue
- dependency and conflict-domain notes needed to decide whether the issue is ready
- validation expectations copied from the accepted contract, not from latent evidence
alone

Keep internal Execution Program ids, graph edges, and goal mechanics out of the user
workflow. They may inform issue readiness, but the Linear issue remains the executable
brief.

## Parallelism Rules

- Split by ownership boundary, validation surface, or deployable behavior, not by
Expand Down Expand Up @@ -78,6 +97,10 @@ dispatch requires a usable briefing surface.
instead of flooding the queue with dependent work.
5. When a lane stops with `decodex:needs-attention`, resolve the recorded blocker
before clearing the label or re-queueing.
6. When the source is a promoted Decision Contract, queue only mapped issues whose
dependencies, conflict domains, acceptance, validation expectations, and registered
workflow state make them ready. Leave blocked, stale, paused, active, terminal, or
unmapped nodes unqueued.

## Boundaries

Expand All @@ -89,3 +112,5 @@ dispatch requires a usable briefing surface.
user asks for automation or the registered workflow requires it.
- Do not use parallelism as a reason to split one atomic behavior across dependent
issues that cannot be validated or reviewed independently.
- Do not promote, accept, or execute a latent research/design result just because it
has proposed issue summaries. Promotion is a separate authority boundary.