- add OPERATIONS table to conductor.agent.md (init, doctor, onboard,
index, memory, telemetry, design, plan, build, verify, integrate,
fast-track) so the conductor agent runs any named operation directly
instead of only the full pipeline
- thin every skill file to a one-line pointer into conductor's
OPERATIONS table, removing duplicated procedure text (contributor
rule: no duplicated behavior in prompts/skills)
- document the operations table in README.md and AGENTS.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot CLI has no ask_user/elicitation tool for custom agents (confirmed
via docs: only execute/read/edit/search/agent/web/todo aliases exist).
The only way to get a live human reply is ending the agent's own turn
with plain text and waiting for the next user message. Conductor was
dumping every design question as one big numbered markdown block in a
single turn, which reads as a form, not a conversation.
Now conductor asks exactly one question per turn (plain text, numbered
candidate answers, one Recommended + Why, an Other: option, a Required
flag), ends its turn, waits for the real reply, then asks the next one.
Decisions are only persisted to decisions.md after the last question is
answered.
Enforce single-entry-point: only conductor is user-invocable now.
architect and reviewer remain fully functional but are only reachable
as subagents that conductor delegates to internally, matching the
same internal-only treatment already given to scout/splitter/builder/
verifier/integrator/scribe.
Confirmed via official Copilot CLI docs: there is no ask_user/elicitation
tool available to custom agents (only execute/read/edit/search/agent/web/
todo aliases exist). Subagent calls made through the 'agent' tool are
stateless — they run to completion and return one final result, with no
mechanism to pause mid-task for a live human reply.
This means conductor invoking interrogator via the agent tool could never
work: interrogator would run as a subagent regardless of whether conductor
itself was foreground or backgrounded, and subagents can't get real user
answers. That's why it was silently writing fabricated decisions.md/
questionnaire.md content instead of actually asking anything.
Fix: delete the interrogator agent entirely and fold its full Q&A
procedure directly into conductor's own DESIGN PHASE, run inline in
conductor's own foreground turn — never delegated. Updated the design
skill/prompt, AGENTS.md, README, QUICKSTART, and role-tier config to
match. Single entry point, no subagent path for anything that needs a
live human answer.
Every agent's PROCEDURE/OUTPUTS referenced lh subcommands vaguely (e.g.
'Run lh run event', 'lh memory get --shard failures'), forcing models to
guess flags at runtime instead of following the profile. Two real bugs
found this way:
- 'lh memory get --shard X' silently ignores --shard (it's a positional
arg on get, not a flag) and returns the whole INDEX.md instead of the
targeted shard. Fixed to 'lh memory get X' everywhere.
- lh run event/lane create calls had no required --type/--status/--id/
--kind/--scope documented, so agents had to trial-and-error discover
them. Added full, exact invocation syntax inline for every command
each agent actually issues (run event, lane create, memory get/put,
host --strategy, graph).
- All agent profiles used the misspelled 'user-invokable' property, which
Copilot CLI silently ignores, so internal-only agents (scout, splitter,
builder, verifier, integrator, scribe) were never actually hidden from
the /agent picker. Renamed to the documented 'user-invocable'.
- conductor/architect instructions said only 'Run lh run event' with no
flags, forcing the model to trial-and-error discover --type/--status/
etc. Documented the exact lh run start/event/end CLI syntax inline.
conductor previously jumped straight to lh host, which succeeds even
without .agents/harness.config.json, then silently hit a hard failure
later reading .agents/memory/INDEX.md (lh doctor confirms: config not
initialised). New projects had no automatic recovery path — lh init
was documented as a manual step users had to remember.
- conductor now runs lh doctor first; if config is missing it runs
lh init --yes (non-interactive defaults) and re-checks, before lh host
- still stops and reports the exact failing check if lh doctor finds
something it can't self-heal (e.g. no verify commands configured)
- README/QUICKSTART updated: lh init/lh doctor documented as optional
manual pre-flight, not a required step, since conductor self-heals
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When no token is found via --token/env/gh CLI, the script now opens
github.com/settings/tokens/new pre-scoped to read:packages with clear
instructions on Note/Expiration/Scopes, falling back to printing the
URL when a browser can't be launched (SSH, containers, CI). Add
--no-open to skip the launch attempt outright.
Also document the flow in README (new 'Generating a GitHub token'
section) and QUICKSTART troubleshooting, and add a hermetic test
covering the --no-open + piped-token path.
Per official docs, agents are invoked via /agent, naming them in a prompt
("Use the conductor agent to..."), or --agent=NAME — never @name. Replaced
all @conductor-style examples. Also documented an observed limitation:
plugin-sourced agents are launchable (by name or --agent flag, both
verified live) but copilot's own 'plugins list' explicitly says custom-agent
introspection isn't finished yet, which likely explains why they aren't
proactively suggested/listed even though they work when named explicitly.
Root-caused live: declaring the agent-plugins.org $schema in plugin.json
opts Copilot CLI into Open Plugin Spec mode, under which the flat
agents/skills fields are silently ignored (0 loaded, no error). Combined
with the pre-existing nested extensions.com.github.copilot.* structure
(also not a real field), the installed plugin contributed zero agents and
zero skills to any consuming project — only this repo's own working copy
worked, because Copilot CLI separately auto-loads .github/agents and
.github/skills for the current git root regardless of any plugin.
Fix: drop $schema entirely, use flat top-level agents/skills/mcpServers
fields (matches the documented, non-spec plugin.json schema). Updated
validate.mjs to error on $schema/extensions instead of recommending them,
and to check the real agents/skills path fields.
plugin.json declared agents/skills under a nested extensions.com.github.copilot
object. The real Copilot CLI plugin schema uses flat top-level agents/skills/
mcpServers fields (verified against official docs). The nested form silently
loaded zero agents and zero skills for any consumer of the installed plugin,
which is why @conductor and /fast-track were unavailable outside this repo
even with the plugin installed and enabled.
scripts/setup-npm-registry.mjs configures npm to pull @redsentech
packages (e.g. @redsentech/lean-harness) from GitHub Packages:
- Discovers a token: --token flag > env (NPM_REGISTRY_TOKEN/
GITHUB_TOKEN/GH_TOKEN) > `gh auth token` > interactive masked prompt
(raw-mode stdin, no echo, no external deps).
- Verifies the token against the GitHub API, reports the authenticated
login, and warns if the OAuth scopes are missing read:packages/
write:packages.
- Writes/updates only its own two lines in .npmrc (scope registry +
auth token), preserving every other line; idempotent on rerun.
- Never prints the full token (masked in all output).
- --dry-run, --unset (clean removal), --local, --scope, --registry,
--skip-verify, --npmrc <path> flags.
- Confirms the result with `npm whoami --registry ...`.
Verified live end-to-end: real GitHub PAT via `gh auth token` ->
verified against api.github.com -> written to a scratch .npmrc ->
`npm whoami` succeeded. Also confirmed the scope-warning is accurate:
installing @redsentech/lean-harness with a token lacking read:packages
correctly 403s, exactly as the script warns it will.
Adds tests/setup-npm-registry.test.mjs (7 hermetic tests: dry-run,
write+preserve, token never printed, idempotent rerun, --unset,
custom scope/registry, clean failure with no token). Adds
"setup-npm-registry" npm script. Documents the script in README (The
`lh` CLI section) and docs/QUICKSTART.md (install + troubleshooting).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add exhaustive table of every lh subcommand: who runs it automatically
(agents, per their own instructions) vs what you run yourself (init,
doctor, report).
- Reorder quickstart to lead with copilot plugin install (marketplace
path, verified end-to-end on this machine), with onboarding script and
clone+link as alternatives and explicit guidance on when to use each.
- Clarify the two-layer install model: plugin install only adds the
behaviour layer; lh CLI (determinism layer) is a separate step until
published to npm.
- README install section and troubleshooting table updated to match.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
\`lh doctor\`'s context7 check is a required, env-only check by design
(ADR: never inferred, never persisted). Tests that shell out to \`lh\`
or \`scripts/onboard.mjs\` inherited whatever CONTEXT7_API_KEY the
developer's shell happened to export, so \`doctor passes once the repo
is initialised\` and the onboard-script e2e test only ever passed on
machines with a real key set — never verified in a clean environment
until this CI run (no secret configured, correctly).
Fixed by injecting an obviously-fake fixture key (TEST_ENV in
helpers.mjs, exported and reused by onboard.test.mjs) into every
subprocess these tests spawn, so behaviour no longer depends on the
ambient shell. Verified locally with \`env -u CONTEXT7_API_KEY\` to
reproduce the CI environment exactly: 58/58 pass either way now.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
\`node --test tests/\` throws MODULE_NOT_FOUND on this Node CLI (it
resolves the bare directory as a CommonJS entry module instead of a
test-runner glob) — this was masked locally all session because every
manual verification used \`node --test tests/*.test.mjs\` directly,
never the actual \`npm test\` script. CI caught it on the first real
run. Fixed package.json's test script to use the explicit glob.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- scripts/onboard.mjs: bootstraps a target repo onto the harness — copies
the behaviour layer (.github/agents, skills, instructions, prompts,
mcp.json, copilot-instructions.md, AGENTS.md), npm links the `lh` CLI,
runs `lh init` + `lh doctor`, prints next steps. Idempotent: identical
files are skipped, differing files require --force, re-running `lh init`
on an initialized repo warns instead of failing. Supports --dry-run and
--no-npm-link for CI/sandboxed use.
- tests/onboard.test.mjs: 5 new e2e tests (dry-run, full run, idempotent
rerun, missing target dir, conflict + --force).
- README: new "Onboarding a new project" section, full lh flag reference
for every subcommand (previously only one-line summaries), Development
section mentions the onboarding script.
Live-tested against ~/Sources/ralph-runtime (a real, not-yet-git-tracked
project): git init, .github/ copied, `npm link` succeeded, `lh init`
ran, `lh doctor` correctly flagged its one real gap (no verify command
configured) rather than a false pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Parallel fleet audit (2 background agents) + direct work:
- src/commands/memory.mjs: `memory put --allow-secrets` was read as
`flags.allowSecrets` (camelCase) but the CLI parser only emits
kebab-case keys, so the flag was always undefined/false. Fixed to
`flags['allow-secrets']`.
- Docs: `lh graph --brief` was referenced 14x across 5 agent.md files,
harness.instructions.md, 4 SKILL.md files, onboard.prompt.md, and
README, but `lh graph` has no --brief flag (terse output is already
the default, --json/--severity are the only flags). Corrected every
reference to match actual CLI surface.
- .github/workflows/ci.yml: run npm install/validate/test on node 20+22.
- Self-dogfooded `lh init --yes` in this repo, producing .agents/
(harness.config.json, architecture.md, conventions.md, memory
shards). `lh doctor` now reports all required checks green.
- Deduped .gitignore harness block against init's auto-managed block.
Verified: 53/53 tests pass, validate 0 errors, doctor all-green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recovered from crashed session (Node OOM). Repo contains full P0-P6
scaffold: plugin.json/marketplace.json, AGENTS.md, ADRs 0001-0006,
lh CLI (init/index/graph/lane/run/memory/host/report/doctor), 10
.github/agents, 12 CLI skills, instructions, context7 mcp.json, and
unit/e2e test suite.
Fixed: run.mjs read --in-tokens/--out-tokens but tests and CLI docs
use --input-tokens/--output-tokens, so telemetry totals were always 0.
Now accepts both forms.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>