Requirements: verifiable custom instructions — make customInstructions findable and checkable
Phase 1 of 3 (requirements → design → tasks). Following the Kiro spec approach (https://kiro.dev/docs/specs/). This phase MUST be reviewed and approved by the required collaborators before moving to design.
Introduction
Issue #132 asks whether the-loop lets a project declare its own rules and guidelines — "and if not, the-loop should allow such specification of a list of files to be understood when the agent-harness like cursor/claude works with it."
It already does. config.customInstructions shipped in issue-59 / decision-029: an ordered list of instruction docs (repo-relative or absolute), each with optional notes, plus an onMissing policy, read at the start of every work item with documented precedence (skills/the-loop/reference/instructions.md).
So the capability is not the deliverable. The deliverable is what the issue's existence proves is missing. The repository's own owner — who merged the feature — could not find it, and had no way to confirm the docs were being read. Two concrete defects follow:
- It is undiscoverable from the front door.
README.mdnever namescustomInstructions, and its enumeration of the skill's reference docs omitsinstructionsentirely (along withcontext,securityandtoken-economy). A reader evaluating the-loop cannot learn the feature exists without reading the schema. - Nothing verifies it.
onMissingis honoured by the agent's good behaviour and by nothing else. A mistyped or moved path silently contributes no guidance; the run looks identical to a correct one. There is no way to ask the harness which docs it actually resolved, soonMissing: erroris a setting that never errors, and a CI job cannot catch a broken registration. The-loop's own idiom for exactly this problem already exists —the-loop scenariosmakes the Gherkin obligation queryable — and custom instructions have no counterpart.
This work item closes both, and changes no existing configuration shape.
Requirements
Requirement 1 — Query the registered instruction docs
User story: As an operator who registered instruction docs, I want to ask the-loop which ones it resolves and whether each is actually readable, so that I can trust the guidance is reaching the agent instead of assuming it.
Acceptance criteria (EARS)
- WHEN the operator runs
the-loop instructionsin a repository THEN the system SHALL report every entry ofcustomInstructions.docs, in configured order, with its configured path, its resolved absolute path, itsnotes, and its state. - WHEN a registered doc exists and is readable THEN the system SHALL report its state as
present. - WHEN a registered doc does not exist at its path THEN the system SHALL report its state as
missing. - WHEN a registered doc exists but cannot be read as text THEN the system SHALL report its state as
unreadable, distinctly frommissing. - WHEN a doc's
pathis absolute THEN the system SHALL use it as given; IF it is relative THEN the system SHALL resolve it against the repository root. - WHEN the repository registers no docs THEN the system SHALL report an empty result and succeed, because configuring nothing is not an error.
- WHEN
--format jsonor--format markdownis passed THEN the system SHALL render the same facts in that format, so a harness or a PR comment can consume them.
Requirement 2 — onMissing becomes an enforceable policy
User story: As an operator who set onMissing: error, I want a broken registration to actually fail, so that the setting means what it says and CI can gate on it.
Acceptance criteria (EARS)
- WHEN a registered doc is
missingorunreadableANDonMissingiserrorTHEN the system SHALL exit non-zero. - WHEN a registered doc is
missingorunreadableANDonMissingiswarn(the default) THEN the system SHALL exit zero and emit a warning naming each unresolved doc. - WHEN a registered doc is
missingorunreadableANDonMissingisignoreTHEN the system SHALL exit zero and emit no warning, while still reporting the doc's state in the output. - WHEN every registered doc is
presentTHEN the system SHALL exit zero under everyonMissingvalue. - IF the harness config is absent, unparseable, or not a mapping THEN the system SHALL report no docs and exit zero, matching the best-effort contract every other repo-scoped reader honours — a half-edited config must not fail a build for a reason unrelated to instructions.
Requirement 3 — The read is declared, documented and pinned
User story: As a maintainer, I want a new CLI read of a repository's harness config to be declared and documented like every other one, so that the read surface stays enumerable.
Acceptance criteria (EARS)
- WHEN the CLI reads
customInstructionsfrom a repository's harness config THEN the system SHALL declare it inharness_config.READSwith the command that reads it and why it is the repository's to declare (decision-044). - WHEN the read is declared THEN it SHALL appear in the CLI-read table of
docs/config/harness-config.md, sotest_harness_config.pyH3/H4 hold in both directions. - WHEN a new command is registered THEN it SHALL have a page under
docs/cli/commands/, sotest_docs_parity.pyP1/P2 hold. - WHEN the harness config is read THEN it SHALL be read through
the_loop.harness_configand no other module, so H2 holds.
Requirement 4 — The feature is discoverable from the front door
User story: As someone evaluating the-loop, I want to learn from the README that it reads my project's own rules, so that I do not have to read the schema to find out.
Acceptance criteria (EARS)
- WHEN a reader reads
README.mdTHEN it SHALL state that the-loop reads the project's own registered instruction docs, and link the instructions reference. - WHEN the README enumerates the skill's reference docs THEN the list SHALL be complete with respect to
skills/the-loop/reference/, so a future reference doc is not silently omitted the wayinstructionswas. - WHEN the reference doc describes the feature THEN it SHALL also describe how to verify a registration, naming the command from R1.
Non-functional requirements
- Pure and CI-safe. The command performs filesystem reads only — no network, no subprocess, no mutation — matching
the-loop checkandthe-loop scenarios. - No new dependency. Rendering reuses the plain-table/markdown/json idiom already in
commands/scenarios.py(minimalism ladder: reuse before introducing). - No config-shape change.
customInstructionskeeps its schema exactly; a repository that upgrades gains a way to check what it already declared.
Security considerations
- Actors & trust: the operator (trusted — they author the harness config) and anyone who can open a PR against the repository (untrusted, because a PR may edit
.the-loop/harness-config.yamland thereby the list of paths). The command itself is run by an operator or by CI. - Trust boundaries & data: two crossings, each a trust boundary the design must enforce with a named mechanism.
- Config path → filesystem read. A
pathvalue decides which file is stat-ed and read. An absolute path is deliberately supported (decision-029: per-machine docs outside the repo), so path traversal is not a vulnerability here — it is the feature. The boundary that matters is therefore output, not access: nothing this command reads may be executed, interpreted, or trusted as instruction by the command. - File content → terminal/JSON output. The command reports facts about docs (path, state, byte count), never their contents. A hostile doc body therefore cannot reach the output at all.
- Doc contents remain what they always were: operator-configured, trusted installation input at the same level as the harness config (
reference/instructions.md§ Security note). This work item does not widen that trust — it narrows the blast radius of a typo, which today is silent.
- Config path → filesystem read. A
- Abuse cases (EARS):
- WHEN a registered
pathpoints outside the repository THEN the system SHALL report its state without reading its contents into the output. - WHEN a registered
pathis a directory, a device node, an unpermitted file, or a binary file THEN the system SHALL reportunreadable— something is there, but it is not a readable instruction doc — and SHALL NOT raise an unhandled exception. 2b. WHEN a registeredpathis a broken symlink THEN the system SHALL reportmissing, because nothing resolves at that path at all. - WHEN
notesorpathcontains terminal control sequences or markdown/JSON metacharacters THEN the system SHALL render them as inert text (escaped for markdown, encoded for JSON) rather than as formatting or control codes. - WHEN the harness config is malformed THEN the system SHALL fail closed to "no docs registered" rather than crash (R2.5).
- WHEN a registered
- Fail closed: an entry that is not a mapping, or that has no usable
path, is reported as an invalid entry and counts as unresolved foronMissing— the-loop never silently drops a registration it could not understand, because silence is the exact defect this work item exists to remove.
Out of scope
- Extending the config shape — globs, directory registration, phase- or path-scoped applicability. All were considered and deferred (YAGNI): #132 asks for "a list of files", which
docs[]already is. A future work item can add them against a schema that is now observable. - Gating the process graph on instruction docs. The obligation is per-work-item ("read every configured doc when starting work"), not per-node, so a
pdlc.yamlhook would be the wrong shape. The command is what the agent and CI call. - Verifying that the agent understood the guidance. Not mechanically checkable; availability is.
Open questions
Scope was confirmed with the requester before the spec was written (#132 comment): answer the question, close the discoverability gap, and add the mechanical check — without extending the config shape.
Review comments
Appended by the-loop's
record-feedbackhook when a human gate approves with comments (issue-109). Append-only and attributed: an approval never silently discards a reviewer's suggestions, and the feedback travels with the document it concerns rather than living in a side-channel tracker.