@timiagama/ecr 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +16 -0
- package/README.md +411 -7
- package/dist/bin.d.ts +20 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +26 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/corpus-loader.d.ts +198 -0
- package/dist/cli/corpus-loader.d.ts.map +1 -0
- package/dist/cli/corpus-loader.js +351 -0
- package/dist/cli/corpus-loader.js.map +1 -0
- package/dist/cli/corpus-statistics.d.ts +113 -0
- package/dist/cli/corpus-statistics.d.ts.map +1 -0
- package/dist/cli/corpus-statistics.js +119 -0
- package/dist/cli/corpus-statistics.js.map +1 -0
- package/dist/cli/diagnostic-reporter.d.ts +103 -0
- package/dist/cli/diagnostic-reporter.d.ts.map +1 -0
- package/dist/cli/diagnostic-reporter.js +254 -0
- package/dist/cli/diagnostic-reporter.js.map +1 -0
- package/dist/cli/project-ignore.d.ts +113 -0
- package/dist/cli/project-ignore.d.ts.map +1 -0
- package/dist/cli/project-ignore.js +199 -0
- package/dist/cli/project-ignore.js.map +1 -0
- package/dist/cli/safe-text.d.ts +25 -0
- package/dist/cli/safe-text.d.ts.map +1 -0
- package/dist/cli/safe-text.js +34 -0
- package/dist/cli/safe-text.js.map +1 -0
- package/dist/cli/supervisor.d.ts +108 -0
- package/dist/cli/supervisor.d.ts.map +1 -0
- package/dist/cli/supervisor.js +139 -0
- package/dist/cli/supervisor.js.map +1 -0
- package/dist/cli.d.ts +247 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +591 -0
- package/dist/cli.js.map +1 -0
- package/dist/corpus-validator.d.ts +183 -0
- package/dist/corpus-validator.d.ts.map +1 -0
- package/dist/corpus-validator.js +479 -0
- package/dist/corpus-validator.js.map +1 -0
- package/dist/document-identity-rule.d.ts +198 -0
- package/dist/document-identity-rule.d.ts.map +1 -0
- package/dist/document-identity-rule.js +244 -0
- package/dist/document-identity-rule.js.map +1 -0
- package/dist/ecr.d.ts +103 -0
- package/dist/ecr.d.ts.map +1 -0
- package/dist/ecr.js +216 -0
- package/dist/ecr.js.map +1 -0
- package/dist/glob-pattern.d.ts +94 -0
- package/dist/glob-pattern.d.ts.map +1 -0
- package/dist/glob-pattern.js +188 -0
- package/dist/glob-pattern.js.map +1 -0
- package/dist/heading-source-form.d.ts +77 -0
- package/dist/heading-source-form.d.ts.map +1 -0
- package/dist/heading-source-form.js +129 -0
- package/dist/heading-source-form.js.map +1 -0
- package/dist/identifier-grammar.d.ts +212 -0
- package/dist/identifier-grammar.d.ts.map +1 -0
- package/dist/identifier-grammar.js +273 -0
- package/dist/identifier-grammar.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-reference-rule.d.ts +557 -0
- package/dist/inline-reference-rule.d.ts.map +1 -0
- package/dist/inline-reference-rule.js +802 -0
- package/dist/inline-reference-rule.js.map +1 -0
- package/dist/meta-documents.d.ts +91 -0
- package/dist/meta-documents.d.ts.map +1 -0
- package/dist/meta-documents.js +133 -0
- package/dist/meta-documents.js.map +1 -0
- package/dist/per-document-visitor.d.ts +510 -0
- package/dist/per-document-visitor.d.ts.map +1 -0
- package/dist/per-document-visitor.js +1032 -0
- package/dist/per-document-visitor.js.map +1 -0
- package/dist/references-section-rule.d.ts +482 -0
- package/dist/references-section-rule.d.ts.map +1 -0
- package/dist/references-section-rule.js +777 -0
- package/dist/references-section-rule.js.map +1 -0
- package/dist/section-hierarchy-rule.d.ts +299 -0
- package/dist/section-hierarchy-rule.d.ts.map +1 -0
- package/dist/section-hierarchy-rule.js +482 -0
- package/dist/section-hierarchy-rule.js.map +1 -0
- package/dist/source-alignment.d.ts +41 -0
- package/dist/source-alignment.d.ts.map +1 -0
- package/dist/source-alignment.js +166 -0
- package/dist/source-alignment.js.map +1 -0
- package/dist/source-lines.d.ts +69 -0
- package/dist/source-lines.d.ts.map +1 -0
- package/dist/source-lines.js +104 -0
- package/dist/source-lines.js.map +1 -0
- package/dist/spec-version.d.ts +19 -0
- package/dist/spec-version.d.ts.map +1 -0
- package/dist/spec-version.js +19 -0
- package/dist/spec-version.js.map +1 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/examples/docs/0. Orientation/0.0 - System Overview.md +34 -0
- package/examples/docs/0. Orientation/0.0.1 - Glossary.md +21 -0
- package/examples/docs/0. Orientation/0.0.2 - Quality Gates.md +22 -0
- package/examples/docs/3. Ingestion/3.1 - Ingestion - Validation Rules.md +33 -0
- package/examples/docs/4. Payments/4.10 - Settlement.md +22 -0
- package/examples/docs/4. Payments/4.2 - Payment Processing Contract.md +33 -0
- package/examples/docs/8. Orchestration/8.1 - Workflow Orchestration Contract.md +38 -0
- package/examples/docs/8. Orchestration/8.1.3 - Retry Policy.md +17 -0
- package/examples/docs/8. Orchestration/8.10 - Dead Letter Queue.md +20 -0
- package/examples/docs/README.md +7 -0
- package/package.json +65 -7
- package/protocol/navigation-protocol.md +223 -0
- package/spec/v2/1 - ECR - Structural Specification.md +1467 -0
- package/spec/v2/2 - ECR - User Guide.md +845 -0
- package/spec/v2/3 - Design Rationale - The Section Separator.md +243 -0
- package/src/bin.ts +30 -0
- package/src/cli/corpus-loader.ts +482 -0
- package/src/cli/corpus-statistics.ts +196 -0
- package/src/cli/diagnostic-reporter.ts +344 -0
- package/src/cli/project-ignore.ts +238 -0
- package/src/cli/safe-text.ts +39 -0
- package/src/cli/supervisor.ts +204 -0
- package/src/cli.ts +836 -0
- package/src/corpus-validator.ts +642 -0
- package/src/document-identity-rule.ts +422 -0
- package/src/ecr.ts +298 -0
- package/src/glob-pattern.ts +242 -0
- package/src/heading-source-form.ts +173 -0
- package/src/identifier-grammar.ts +380 -0
- package/src/index.ts +78 -0
- package/src/inline-reference-rule.ts +1235 -0
- package/src/meta-documents.ts +159 -0
- package/src/per-document-visitor.ts +1528 -0
- package/src/references-section-rule.ts +1182 -0
- package/src/section-hierarchy-rule.ts +705 -0
- package/src/source-alignment.ts +209 -0
- package/src/source-lines.ts +123 -0
- package/src/spec-version.ts +19 -0
- package/src/types.ts +267 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 0.0 - System Overview
|
|
2
|
+
|
|
3
|
+
A payments platform that ingests merchant settlement files, validates them,
|
|
4
|
+
and processes the resulting payment instructions.
|
|
5
|
+
|
|
6
|
+
## 0.0#1 - Purpose
|
|
7
|
+
|
|
8
|
+
This document is the orientation map. It carries no normative rules of its own;
|
|
9
|
+
each domain is governed by its own authority document.
|
|
10
|
+
|
|
11
|
+
## 0.0#2 - Mental Model
|
|
12
|
+
|
|
13
|
+
### 0.0#2.1 - The Happy Path
|
|
14
|
+
|
|
15
|
+
A settlement file arrives, is validated per 3.1, and produces payment
|
|
16
|
+
instructions executed per 4.2.
|
|
17
|
+
|
|
18
|
+
### 0.0#2.2 - Failure Handling
|
|
19
|
+
|
|
20
|
+
Every stage runs under the orchestration contract, so retry semantics are
|
|
21
|
+
uniform across the platform — see 8.1#3.
|
|
22
|
+
|
|
23
|
+
## 0.0#3 - Where To Start
|
|
24
|
+
|
|
25
|
+
New engineers should read 0.0.1 before anything else. Build and review
|
|
26
|
+
expectations are defined in 0.0.2.
|
|
27
|
+
|
|
28
|
+
## References
|
|
29
|
+
|
|
30
|
+
- 0.0.1 - Glossary (dependency - defines the vocabulary used throughout this overview)
|
|
31
|
+
- 0.0.2 - Quality Gates (dependency - the build and review expectations referenced here)
|
|
32
|
+
- 3.1 - Ingestion - Validation Rules (dependency - the validation stage described in this overview)
|
|
33
|
+
- 4.2 - Payment Processing Contract (dependency - the execution stage described in this overview)
|
|
34
|
+
- 8.1 - Workflow Orchestration Contract (authority - governs the runtime behaviour of every stage)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 0.0.1 - Glossary
|
|
2
|
+
|
|
3
|
+
Vocabulary shared across the corpus. Terms defined here are used with this
|
|
4
|
+
meaning everywhere; a document that needs a narrower meaning must say so.
|
|
5
|
+
|
|
6
|
+
## 0.0.1#1 - Settlement File
|
|
7
|
+
|
|
8
|
+
A merchant-submitted batch of transactions awaiting reconciliation.
|
|
9
|
+
|
|
10
|
+
## 0.0.1#2 - Payment Instruction
|
|
11
|
+
|
|
12
|
+
A single validated, executable transfer derived from a settlement file.
|
|
13
|
+
|
|
14
|
+
## 0.0.1#3 - Idempotency Key
|
|
15
|
+
|
|
16
|
+
The value that makes retrying a payment instruction safe. Its derivation is
|
|
17
|
+
defined per 4.2#1.1 and must not be reimplemented elsewhere.
|
|
18
|
+
|
|
19
|
+
## References
|
|
20
|
+
|
|
21
|
+
- 4.2 - Payment Processing Contract (contract - defines idempotency key derivation referenced here)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 0.0.2 – Quality Gates
|
|
2
|
+
|
|
3
|
+
Build and review expectations. This document uses an en dash in its H1 to
|
|
4
|
+
demonstrate that the separator variant carries no structural meaning.
|
|
5
|
+
|
|
6
|
+
## 0.0.2#1 - Purpose
|
|
7
|
+
|
|
8
|
+
To state what "done" means, so that review is a check rather than a negotiation.
|
|
9
|
+
|
|
10
|
+
## 0.0.2#2 — Test Coverage
|
|
11
|
+
|
|
12
|
+
Every behavioural change ships with a test in the standing suite. A throwaway
|
|
13
|
+
script is not coverage.
|
|
14
|
+
|
|
15
|
+
## 0.0.2#3 - Review Order
|
|
16
|
+
|
|
17
|
+
Implementation and review happen in separate sessions. See 0.0#3 for where this
|
|
18
|
+
sits in onboarding.
|
|
19
|
+
|
|
20
|
+
## References
|
|
21
|
+
|
|
22
|
+
- 0.0 - System Overview (dependency - the orientation map that points here)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 3.1 - Ingestion - Validation Rules
|
|
2
|
+
|
|
3
|
+
Authoritative rules for accepting or rejecting a settlement file. A file that
|
|
4
|
+
fails any rule here never reaches payment processing.
|
|
5
|
+
|
|
6
|
+
## 3.1#1 - Structural Validation
|
|
7
|
+
|
|
8
|
+
### 3.1#1.1 - Required Columns
|
|
9
|
+
|
|
10
|
+
A settlement file must carry merchant identifier, amount, currency and
|
|
11
|
+
transaction reference. Missing columns reject the whole file, not the row.
|
|
12
|
+
|
|
13
|
+
### 3.1#1.2 - Encoding
|
|
14
|
+
|
|
15
|
+
Files are UTF-8. A file that fails to decode is rejected without partial
|
|
16
|
+
processing.
|
|
17
|
+
|
|
18
|
+
## 3.1#2 - Semantic Validation
|
|
19
|
+
|
|
20
|
+
Amounts must be positive integers in minor units. Currency must be ISO 4217.
|
|
21
|
+
|
|
22
|
+
## 3.1#3 - Rejection Reporting
|
|
23
|
+
|
|
24
|
+
This stage is invoked under the orchestration contract, per 8.1.
|
|
25
|
+
|
|
26
|
+
A rejected file produces one report naming every failed rule. Rejection is not
|
|
27
|
+
retried automatically — retry semantics apply to transient failures only,
|
|
28
|
+
per 8.1#3.
|
|
29
|
+
|
|
30
|
+
## References
|
|
31
|
+
|
|
32
|
+
- 0.0.1 - Glossary (dependency - defines settlement file and payment instruction)
|
|
33
|
+
- 8.1 - Workflow Orchestration Contract (constraint - retry semantics that bound this stage)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 4.10 — Settlement
|
|
2
|
+
|
|
3
|
+
Reconciliation of executed payment instructions against merchant expectations.
|
|
4
|
+
This document's DocID shares a prefix with 4.1 in text but is a distinct
|
|
5
|
+
document; identifier matching is by segment, not by string prefix.
|
|
6
|
+
|
|
7
|
+
## 4.10#1 - Reconciliation Window
|
|
8
|
+
|
|
9
|
+
Settlement runs daily. An instruction executed after the cut-off settles in the
|
|
10
|
+
next window.
|
|
11
|
+
|
|
12
|
+
## 4.10#2 - Discrepancies
|
|
13
|
+
|
|
14
|
+
Settlement runs as an orchestrated stage; see 8.1 for invocation and retry.
|
|
15
|
+
|
|
16
|
+
A discrepancy halts settlement for that merchant only. Orchestration-level
|
|
17
|
+
retry does not apply — see 8.1#3.2.
|
|
18
|
+
|
|
19
|
+
## References
|
|
20
|
+
|
|
21
|
+
- 4.2 - Payment Processing Contract (dependency - the upstream stage that produces settled instructions)
|
|
22
|
+
- 8.1 - Workflow Orchestration Contract (constraint - bounds the retry behaviour excluded here)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 4.2 - Payment Processing Contract
|
|
2
|
+
|
|
3
|
+
The contract every payment executor must satisfy. This document defines
|
|
4
|
+
obligations, not implementation.
|
|
5
|
+
|
|
6
|
+
## 4.2#1 - Idempotency
|
|
7
|
+
|
|
8
|
+
### 4.2#1.1 - Key Derivation
|
|
9
|
+
|
|
10
|
+
The idempotency key is the SHA-256 of merchant identifier, transaction
|
|
11
|
+
reference and amount in minor units, joined by U+001F. This derivation is
|
|
12
|
+
normative; see 0.0.1#3 for the term itself.
|
|
13
|
+
|
|
14
|
+
### 4.2#1.2 - Replay
|
|
15
|
+
|
|
16
|
+
A repeated instruction carrying a known key returns the original result and
|
|
17
|
+
performs no new transfer.
|
|
18
|
+
|
|
19
|
+
## 4.2#2 - Ordering
|
|
20
|
+
|
|
21
|
+
Instructions within a settlement file are independent and may execute in any
|
|
22
|
+
order. Cross-file ordering is not guaranteed.
|
|
23
|
+
|
|
24
|
+
## 4.2#3 - Settlement Handoff
|
|
25
|
+
|
|
26
|
+
Completed instructions are handed to settlement per 4.10.
|
|
27
|
+
|
|
28
|
+
## References
|
|
29
|
+
|
|
30
|
+
- 0.0.1 - Glossary (dependency - defines the idempotency key term this contract derives)
|
|
31
|
+
- 3.1 - Ingestion - Validation Rules (authority - only validated instructions reach this contract)
|
|
32
|
+
- 4.10 - Settlement (dependency - the downstream stage this contract hands off to)
|
|
33
|
+
- 8.1 - Workflow Orchestration Contract (authority - governs execution and retry of this contract)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 8.1 - Workflow Orchestration Contract
|
|
2
|
+
|
|
3
|
+
Governs how every stage in the platform runs: invocation, failure
|
|
4
|
+
classification and retry. This document is an authority for the runtime
|
|
5
|
+
behaviour of the stages it governs; it does not define what those stages do.
|
|
6
|
+
|
|
7
|
+
## 8.1#1 - Stage Invocation
|
|
8
|
+
|
|
9
|
+
A stage is invoked with a single immutable input and returns a single result.
|
|
10
|
+
Stages do not share mutable state.
|
|
11
|
+
|
|
12
|
+
## 8.1#2 - Failure Classification
|
|
13
|
+
|
|
14
|
+
### 8.1#2.1 - Transient Failures
|
|
15
|
+
|
|
16
|
+
Network timeouts, lock contention and rate limiting. Eligible for retry.
|
|
17
|
+
|
|
18
|
+
### 8.1#2.2 - Permanent Failures
|
|
19
|
+
|
|
20
|
+
Validation rejections and contract violations. Never retried; see 3.1#3.
|
|
21
|
+
|
|
22
|
+
## 8.1#3 - Retry Semantics
|
|
23
|
+
|
|
24
|
+
### 8.1#3.1 - Backoff
|
|
25
|
+
|
|
26
|
+
Exponential with full jitter, base 200ms, ceiling 30s.
|
|
27
|
+
|
|
28
|
+
### 8.1#3.2 - Bounds
|
|
29
|
+
|
|
30
|
+
At most five attempts. A stage that exhausts its attempts fails permanently and
|
|
31
|
+
is reported, not re-queued. The concrete policy is defined per 8.1.3, and the
|
|
32
|
+
exhausted invocation is routed per 8.10.
|
|
33
|
+
|
|
34
|
+
## References
|
|
35
|
+
|
|
36
|
+
- 3.1 - Ingestion - Validation Rules (dependency - the rejection behaviour classified as permanent here)
|
|
37
|
+
- 8.1.3 - Retry Policy (contract - the concrete policy values this contract delegates to)
|
|
38
|
+
- 8.10 - Dead Letter Queue (dependency - where exhausted invocations are routed)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 8.1.3 - Retry Policy
|
|
2
|
+
|
|
3
|
+
Concrete retry values. This document's DocID extends 8.1, and its sections are
|
|
4
|
+
addressed as 8.1.3#N — distinct from 8.1's own sections, which are 8.1#N.
|
|
5
|
+
|
|
6
|
+
## 8.1.3#1 - Bounds
|
|
7
|
+
|
|
8
|
+
Maximum five attempts per stage invocation, as required per 8.1#3.2.
|
|
9
|
+
|
|
10
|
+
## 8.1.3#2 - Backoff Parameters
|
|
11
|
+
|
|
12
|
+
Base 200ms, ceiling 30s, full jitter. Values are configuration, not contract;
|
|
13
|
+
changing them does not change 8.1.
|
|
14
|
+
|
|
15
|
+
## References
|
|
16
|
+
|
|
17
|
+
- 8.1 - Workflow Orchestration Contract (authority - defines the retry semantics these values implement)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 8.10 - Dead Letter Queue
|
|
2
|
+
|
|
3
|
+
Where permanently failed stage invocations go. Its DocID begins with the same
|
|
4
|
+
characters as 8.1 but is a different document: identifiers compare by segment,
|
|
5
|
+
never by string prefix.
|
|
6
|
+
|
|
7
|
+
## 8.10#1 - Entry Conditions
|
|
8
|
+
|
|
9
|
+
A stage invocation enters the queue when it fails permanently, or when it
|
|
10
|
+
exhausts its retry attempts as bounded per 8.1#3.2.
|
|
11
|
+
|
|
12
|
+
## 8.10#2 - Replay
|
|
13
|
+
|
|
14
|
+
Replay is manual and produces a fresh idempotency key only when the original
|
|
15
|
+
instruction has been superseded — see 4.2#1.
|
|
16
|
+
|
|
17
|
+
## References
|
|
18
|
+
|
|
19
|
+
- 4.2 - Payment Processing Contract (dependency - idempotency behaviour on replay)
|
|
20
|
+
- 8.1 - Workflow Orchestration Contract (authority - defines the failure classification that routes here)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Example Corpus
|
|
2
|
+
|
|
3
|
+
A small ECR-compliant corpus used as a worked example and as the fixture for
|
|
4
|
+
this repository's navigation-recipe tests.
|
|
5
|
+
|
|
6
|
+
This file is a meta-document: it carries no DocID and sits outside the reference
|
|
7
|
+
graph. `ecr lint` excludes it by default.
|
package/package.json
CHANGED
|
@@ -1,18 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timiagama/ecr",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Explicit Constraint Referencing (ECR): a Markdown convention that helps reduce coding-agent errors by turning cross-references in architecture docs into a graph that agents can navigate with grep. Agents gain IDE-like \"Go to Definition\" and \"Find all references\" across large document corpora, reducing missed constraints and context-window bloat.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ecr": "./dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src",
|
|
14
|
+
"spec",
|
|
15
|
+
"protocol",
|
|
16
|
+
"examples",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"NOTICE"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"markdown",
|
|
26
|
+
"documentation",
|
|
27
|
+
"architecture",
|
|
28
|
+
"coding-agent",
|
|
29
|
+
"ai-agent",
|
|
30
|
+
"llm",
|
|
31
|
+
"context-engineering",
|
|
32
|
+
"constraint",
|
|
33
|
+
"linter",
|
|
34
|
+
"ecr"
|
|
35
|
+
],
|
|
7
36
|
"repository": {
|
|
8
37
|
"type": "git",
|
|
9
38
|
"url": "git+https://github.com/timiagama/ecr.git"
|
|
10
39
|
},
|
|
11
40
|
"homepage": "https://github.com/timiagama/ecr#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/timiagama/ecr/issues"
|
|
43
|
+
},
|
|
44
|
+
"author": "Timi Agama",
|
|
45
|
+
"license": "Apache-2.0",
|
|
12
46
|
"publishConfig": {
|
|
13
47
|
"access": "public"
|
|
14
48
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "npm run clean && npm run generate:protocol && tsc",
|
|
51
|
+
"generate:protocol": "node scripts/generate-navigation-protocol.mjs",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"lint": "eslint src/ tests/",
|
|
54
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.eslint.json",
|
|
55
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
56
|
+
"prepublishOnly": "npm run verify",
|
|
57
|
+
"verify": "npm run typecheck && npm run lint && npm test && npm run build && node dist/bin.js lint spec/v2 && node dist/bin.js lint docs/engineering && node dist/bin.js lint --example"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"decode-named-character-reference": "^1.3.0",
|
|
61
|
+
"micromark-util-decode-numeric-character-reference": "^2.0.2",
|
|
62
|
+
"remark-parse": "^11.0.0",
|
|
63
|
+
"unified": "^11.0.5"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@eslint/js": "^9.39.2",
|
|
67
|
+
"@types/node": "^22.10.0",
|
|
68
|
+
"@vscode/ripgrep": "^1.18.0",
|
|
69
|
+
"eslint": "^9.39.2",
|
|
70
|
+
"eslint-plugin-jsdoc": "^62.5.5",
|
|
71
|
+
"eslint-plugin-tsdoc": "^0.5.0",
|
|
72
|
+
"typescript": "~6.0.3",
|
|
73
|
+
"typescript-eslint": "^8.55.0",
|
|
74
|
+
"vitest": "^4.0.18"
|
|
75
|
+
}
|
|
18
76
|
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# ECR Navigation Protocol for Coding Agents
|
|
2
|
+
|
|
3
|
+
You are grounding an implementation task against a documentation corpus written
|
|
4
|
+
with **Explicit Constraint Referencing (ECR)**. **Do not load the corpus
|
|
5
|
+
wholesale.** Every architectural document carries a stable numeric identity (a
|
|
6
|
+
*DocID*), every sub-heading carries a *SectionID*, and every document declares its
|
|
7
|
+
external references, each with a type. That structure lets you walk to exactly
|
|
8
|
+
the sections a task depends on, and stop.
|
|
9
|
+
|
|
10
|
+
Read this once when a task starts, then navigate on demand. Keep the corpus out
|
|
11
|
+
of your context except the sections you are actively using.
|
|
12
|
+
|
|
13
|
+
## 1 - What the structure gives you
|
|
14
|
+
|
|
15
|
+
**DocID.** The number in a document's H1:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
# 4.2 - Payment Processing Contract
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- DocIDs are globally unique and stable. The DocID **is** the document's
|
|
22
|
+
identity — not its title, not its path.
|
|
23
|
+
- Many corpora also start each filename with its DocID and group documents
|
|
24
|
+
into folders by first segment (`4. Payments/4.2 - Payment Processing
|
|
25
|
+
Contract.md`). Where yours does, you can jump to a directory from the number
|
|
26
|
+
alone — but ECR does not require it, so when the path and the H1 disagree,
|
|
27
|
+
the H1 is authoritative.
|
|
28
|
+
- DocIDs may be any depth, and one DocID may be a prefix of another. `8.1` and
|
|
29
|
+
`8.1.3` may both be documents, and `8.10` is a third, unrelated to either.
|
|
30
|
+
**Compare identifiers by segment, never by string prefix.**
|
|
31
|
+
|
|
32
|
+
**SectionID.** A DocID, then `#`, then a dot-separated section path:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
## 8.1#3 - Retry Semantics
|
|
36
|
+
### 8.1#3.1 - Backoff
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`8.1#3.1` is document `8.1`, section 3, sub-section 1. The `#` is what makes the
|
|
40
|
+
identifier unambiguous: everything before it is the document, everything after
|
|
41
|
+
it is the path within that document. A heading at depth *d* carries *d − 1*
|
|
42
|
+
section-path segments.
|
|
43
|
+
|
|
44
|
+
**SectionID stability.** SectionIDs are identities, not sequence numbers. They need
|
|
45
|
+
not be contiguous or appear in numeric order, so do not renumber existing sections
|
|
46
|
+
merely to remove gaps or reflect document order. Existing SectionIDs remain stable
|
|
47
|
+
when unrelated sections are inserted or reordered. Nested SectionIDs encode their
|
|
48
|
+
parent path, however: moving a subsection under a different parent, or promoting it
|
|
49
|
+
to another heading level, changes its identity and requires affected references to
|
|
50
|
+
be updated. Prefer a top-level SectionID for a consumer reference when it expresses
|
|
51
|
+
the required constraint precisely enough; use a subsection address when the narrower
|
|
52
|
+
rule is what the consumer actually depends on.
|
|
53
|
+
|
|
54
|
+
**References.** Every ECR document carries a `## References` section
|
|
55
|
+
declaring its typed, document-level references. Read it before acting on the
|
|
56
|
+
document. References entries cite documents, never sections.
|
|
57
|
+
|
|
58
|
+
**Inline references.** `see X` and `per X` point into another document, either
|
|
59
|
+
at a whole document (`see 8.1`) or at a precise section (`per 8.1#3.2`). They
|
|
60
|
+
may be capitalised at the start of a sentence, so match `[Ss]ee` and `[Pp]er`.
|
|
61
|
+
|
|
62
|
+
**Meta-documents.** Repository-level instruction and orientation files such as
|
|
63
|
+
`README`, `CLAUDE.md`, `AGENTS.md`, contributing guides and changelogs may sit
|
|
64
|
+
outside the reference graph and therefore carry no DocID. Do not assume every
|
|
65
|
+
Markdown file in or near a corpus is an ECR document.
|
|
66
|
+
|
|
67
|
+
## 2 - How to find things
|
|
68
|
+
|
|
69
|
+
Patterns are ripgrep. Escape the dots in an identifier. If `rg` is not
|
|
70
|
+
installed, GNU grep takes the same patterns: use `grep -rnE` for `rg --no-ignore -n`,
|
|
71
|
+
`grep -rlE` for `rg --no-ignore -l`, and `grep -rnP` for `rg --no-ignore -P -n`.
|
|
72
|
+
|
|
73
|
+
`--no-ignore` makes ripgrep also search files listed in `.gitignore`, `.ignore`
|
|
74
|
+
and `.rgignore`, which the CLI validates. Without it, a valid document can be
|
|
75
|
+
invisible to your search.
|
|
76
|
+
|
|
77
|
+
These searches do not follow symbolic links or junctions inside the corpus, and
|
|
78
|
+
the `ecr` CLI does not validate what is behind them. A corpus directory named on
|
|
79
|
+
the command line is followed even when it is itself a link, by the searches and
|
|
80
|
+
the CLI alike.
|
|
81
|
+
|
|
82
|
+
**Open the document for a DocID.**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
rg --no-ignore -l "^# 8\.1[^0-9.#]" docs # the H1 for 8.1, not 8.10 or 8.1.3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Resolve a SectionID** — jump straight to the heading:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
rg --no-ignore -n "^#+ 8\.1#3\.1([^0-9]|$)" docs
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Read a document's declared references (forward edges):**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
rg -n -A 40 "^## References" "docs/8. Orchestration/8.1 - Workflow Orchestration Contract.md"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Find who relies on a document, or on one specific section (reverse edges).**
|
|
101
|
+
Nothing precomputes this — grep the citations. Run this *before editing a
|
|
102
|
+
section* to see exactly who leans on that spot.
|
|
103
|
+
|
|
104
|
+
| Question | Pattern |
|
|
105
|
+
|---|---|
|
|
106
|
+
| Who references document `8.1`? | `rg --no-ignore -n "(\b\|_)([Ss]ee\|[Pp]er) 8\.1(\.[^0-9A-Za-z.#]\|\.$\|[^0-9A-Za-z.#]\|$)" docs` |
|
|
107
|
+
| Who references any section of `8.1`? | `rg --no-ignore -n "(\b\|_)([Ss]ee\|[Pp]er) 8\.1#" docs` |
|
|
108
|
+
| Who references section `8.1#3` or below? | `rg --no-ignore -n "(\b\|_)([Ss]ee\|[Pp]er) 8\.1#3([^0-9A-Za-z#]\|$)" docs` |
|
|
109
|
+
| Either the document or any section? | `rg --no-ignore -n "(\b\|_)([Ss]ee\|[Pp]er) 8\.1(#[0-9.]*\|\.[^0-9A-Za-z.#]\|\.$\|[^0-9A-Za-z.#]\|$)" docs` |
|
|
110
|
+
| Every section-precise reference in the corpus | `rg --no-ignore -n "(\b\|_)([Ss]ee\|[Pp]er) [0-9.]+#" docs` |
|
|
111
|
+
| References-section entries citing `8.1` | `rg --no-ignore -n "^\s*([-*+]|[0-9]+[.)])\s*\[?8\.1[^0-9.#]" docs` |
|
|
112
|
+
|
|
113
|
+
These patterns bound a citation explicitly, because `\b` alone fails at both
|
|
114
|
+
ends. It cannot terminate a dotted identifier: `.` and `#` are both non-word
|
|
115
|
+
characters, so `8\.1\b` matches inside `8.1.3`, `8.1#3` and `8.1.3#1`. And `_`
|
|
116
|
+
is a word character, so `\b` finds nothing before the keyword in `_see 8.1_`,
|
|
117
|
+
which is ordinary Markdown emphasis. So the keyword follows `(\b|_)` — a word
|
|
118
|
+
boundary or an underscore — and the identifier is followed by anything but an
|
|
119
|
+
ASCII letter or digit. After a bare DocID, `#` is refused too, and so is `.`
|
|
120
|
+
unless it ends a sentence (the `\.[^0-9A-Za-z.#]` / `\.$` alternatives), so that
|
|
121
|
+
`see 8.1.` matches while `see 8.1.3` does not.
|
|
122
|
+
|
|
123
|
+
Where ripgrep is built with PCRE2, `-P` allows the clearer form:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
rg --no-ignore -P -n "(?<![0-9A-Za-z])([Ss]ee|[Pp]er) 8\.1(?![0-9A-Za-z#])(?!\.[0-9A-Za-z.#])" docs
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Find everything a document governs.** This is the high-value query that
|
|
130
|
+
similarity search cannot answer — a generic contract governing many specific
|
|
131
|
+
documents that don't resemble it textually:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
rg --no-ignore -ln "^\s*([-*+]|[0-9]+[.)])\s*\[?8\.1[^0-9.#].*\(authority" docs
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Swap `authority` for `constraint` / `contract` / `dependency` to pull the other
|
|
138
|
+
edge types.
|
|
139
|
+
|
|
140
|
+
**Map the whole corpus, one line per document:**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
rg --no-ignore -n "^# [0-9]" docs
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Find the document owning a concept by name** — the fallback when you have no
|
|
147
|
+
ID to follow:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
rg --no-ignore -li "idempotency key" docs
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## 3 - What each reference obligates you to do
|
|
154
|
+
|
|
155
|
+
A References entry reads `{DocID} - {Title} ({direction} - {explanation})`. The
|
|
156
|
+
edge points **from this document to the cited one**. The middle column is what
|
|
157
|
+
ECR defines; the right-hand column is how to act on it, which ECR does not
|
|
158
|
+
define:
|
|
159
|
+
|
|
160
|
+
| Direction | Meaning (ECR) | Agent action (recommended) |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| **authority** | the cited document governs this one | Read it first. Treat it as the governing source. Flag apparent conflicts. |
|
|
163
|
+
| **constraint** | the cited document restricts this one | Check your change does not violate it. |
|
|
164
|
+
| **contract** | the cited document defines obligations or interfaces | Conform exactly — schemas, field names, typed outputs. |
|
|
165
|
+
| **dependency** | this document consumes the cited one | Read it to use it correctly. |
|
|
166
|
+
|
|
167
|
+
The `{explanation}` says *why* the edge exists. Use it to judge whether an edge
|
|
168
|
+
is relevant to your current change before you open the target.
|
|
169
|
+
|
|
170
|
+
**Precedence.** ECR does not define one. It records that a document governs
|
|
171
|
+
another; it does not say what to do when two governing documents disagree,
|
|
172
|
+
because resolving that requires reading what the constraints mean, which is
|
|
173
|
+
outside what ECR claims. Authority is therefore domain-scoped rather than a
|
|
174
|
+
ranking: each authority document governs its own domain and none outranks
|
|
175
|
+
another.
|
|
176
|
+
|
|
177
|
+
So when two authorities appear to conflict, surface it rather than picking a
|
|
178
|
+
winner. If the corpus declares its own precedence hierarchy, it does so in a map
|
|
179
|
+
document of its own — follow that. If it does not, say so and ask.
|
|
180
|
+
|
|
181
|
+
## 4 - Grounding protocol
|
|
182
|
+
|
|
183
|
+
For any implementation task:
|
|
184
|
+
|
|
185
|
+
1. **Orient.** Identify the DocID(s) the task touches.
|
|
186
|
+
2. **Open the primary document** by its DocID.
|
|
187
|
+
3. **Read its `## References` before writing code.** Open every `authority` and
|
|
188
|
+
`constraint` it cites; treat `contract` entries as obligations to satisfy;
|
|
189
|
+
read `dependency` entries you will consume. Use the `{explanation}` to skip
|
|
190
|
+
edges irrelevant to your change.
|
|
191
|
+
4. **Follow inline `see`/`per` to the exact SectionID named** — open that one
|
|
192
|
+
section, not the whole document.
|
|
193
|
+
5. **Before finishing, reverse-check at section granularity.** Grep backlinks to
|
|
194
|
+
the section you changed using the reverse-lookup patterns above. This is the step that surfaces the documents
|
|
195
|
+
relying on what you changed — the ones your target never mentions, and the
|
|
196
|
+
ones a change can silently break, because nothing in the file you are
|
|
197
|
+
reading points at them.
|
|
198
|
+
6. **Load only what you need.** Pull sections, not the corpus. A few extra reads
|
|
199
|
+
of the right slice beat one read of everything.
|
|
200
|
+
7. **On ambiguity, defer** to the domain authority rather than guessing.
|
|
201
|
+
|
|
202
|
+
## 5 - Gotchas
|
|
203
|
+
|
|
204
|
+
- **Match numbers, not dashes.** Separators appear as hyphen `-`, en dash `–`
|
|
205
|
+
and em dash `—`, sometimes mixed within one line. Never anchor a pattern on a
|
|
206
|
+
dash.
|
|
207
|
+
- **Never match an identifier by string prefix.** `8.1` is not a prefix of
|
|
208
|
+
`8.10` in identifier terms, and `8.1.3` is a different document from section
|
|
209
|
+
`8.1#3`. Use the lookup patterns in this protocol rather than improvising.
|
|
210
|
+
- **`see` and `per` may be capitalised.** A pattern matching only lowercase
|
|
211
|
+
silently under-reports backlinks, which is the one failure this protocol
|
|
212
|
+
exists to prevent.
|
|
213
|
+
- **A bare number in prose is not an edge.** Only `see`/`per` inline references
|
|
214
|
+
and `## References` entries are load-bearing. But opening a document is cheap —
|
|
215
|
+
when unsure, look.
|
|
216
|
+
- **Every reference is found, but not every hit is a reference.** In a corpus
|
|
217
|
+
that passes the linter, these patterns find every reference it recognises.
|
|
218
|
+
They can also match text it does not: `per 60**s**` reads as `per 60s`, which
|
|
219
|
+
is prose, yet the pattern for `60` matches it. Read the line before treating
|
|
220
|
+
a hit as an edge.
|
|
221
|
+
- **Corpora drift.** A document may deviate from the convention. If a recipe
|
|
222
|
+
returns nothing, loosen it — drop the anchor, search by title text — before
|
|
223
|
+
concluding the target does not exist.
|