spec 1.0 · chuẩn tắc

Bản đặc tả Leji

Toàn bộ bản đặc tả chuẩn tắc được gom trên một trang theo thứ tự đọc, thuận tiện để đọc liền mạch hoặc tìm kiếm. Mỗi phần đều dẫn về trang riêng, nơi từng tiêu đề có neo trích dẫn.

Bản đặc tả Leji ↗pull request

Bản đặc tả Leji

Leji là một bản đặc tả mở cho lớp ngữ cảnh dùng chung của các nhóm AI-native. Nó định nghĩa cách một nhóm lưu trữ, quản trị, nạp và bảo trì phần ngữ cảnh thuộc về kho mã nguồn mà cả người lẫn AI agent đều đọc trong mọi tác vụ.

Phiên bản đặc tả 1.0.0
Trạng thái GA, đã đóng băng ở bản phát hành công cụ tham chiếu v1.3.0. Thay đổi phá vỡ tương thích đòi hỏi một phiên bản major mới.
Chủ biên Vuong Nguyen
Một trang Toàn bộ bản đặc tả trên một trang duy nhất

Nguyên tắc (tham khảo)#

  1. Chủ ý thay vì chỉ dẫn. Leji ghi lại chủ ý bền vững (mọi thứ nghĩa là gì, điều gì phải luôn đúng, vì sao lại như vậy), thay vì các chỉ dẫn mang tính mệnh lệnh gắn với từng nhà cung cấp. Người và agent tự suy ra hành động từ chủ ý đã khai báo và ngữ cảnh của tác vụ.
  2. Một vòng tròn, không phải một tầng. Người với người, người với AI, và người với AI với người đều là những luồng hạng nhất quanh cùng một lớp ngữ cảnh. Bình đẳng về quyền đọc, không bình đẳng về quyền quyết: ai có quyền truy cập một lớp ngữ cảnh thì đọc được toàn bộ nó, ai cũng có thể đề xuất, và người là bên phê duyệt. Việc tham gia dựa trên vai trò chứ không dựa trên công cụ: một bên tham gia chưa từng trực tiếp động tới git vẫn là thành viên hạng nhất của vòng tròn. Bản thân quyền truy cập là việc của hệ quản lý phiên bản, không phải của Leji; vòng tròn được giới hạn trong nhóm đối tượng của một lớp ngữ cảnh.
  3. Cơ chế thay vì thiện chí. Ngữ cảnh dùng chung mặc nhiên sẽ dần xuống cấp: thực tế thay đổi còn tài liệu thì không, và chẳng có gì buộc wiki phải luôn cập nhật. Việc cưỡng chế của Leji nằm trong cơ chế vận hành, không dựa vào thiện chí: mọi thay đổi đi qua cùng cổng xem xét như code, công cụ báo lỗi khi phát hiện trôi lệch máy kiểm được, hạn tươi làm lộ những gì đã cũ, và ngữ cảnh lỗi thời không bao giờ bị âm thầm coi là còn hiệu lực (về mặt chuẩn tắc, xem governance.md, phần Độ tươi).

Phần còn lại của bản đặc tả này là hệ quả chuẩn tắc của ba nguyên tắc trên.

Ngôn ngữ tuân thủ#

Các từ khoá MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAYOPTIONAL trong bản đặc tả này được hiểu theo đúng mô tả trong RFC 2119. Bản dịch này giữ nguyên các từ khoá tiếng Anh, vì chúng là thuật ngữ có định nghĩa chặt chẽ: MUST là bắt buộc, MUST NOT là cấm, SHOULD là nên, SHOULD NOT là không nên, MAY là tuỳ chọn.

Cách trích dẫn bản đặc tả này (tham khảo)#

Trích dẫn một mục theo tiêu đề của mục đó kèm phiên bản đặc tả, cùng một permalink tới neo của mục. Trên trang đặc tả, mỗi tiêu đề đều hiện neo của nó khi rê chuột lên.

  • Định dạng: Leji 1.0, §Tên mục: https://leji.org/spec/<document>/#<anchor>
  • Ví dụ: Leji 1.0, §The circle, normatively: https://leji.org/spec/governance/#the-circle-normatively

Hãy luôn trích dẫn kèm phiên bản (Leji 1.0): thay đổi phá vỡ tương thích sẽ ra mắt dưới dạng một phiên bản major mới, nên một trích dẫn có ghim phiên bản vẫn còn chính xác sau khi bản đặc tả tiến hoá. Trích dẫn trỏ tới trang tiếng Anh, vì đó mới là văn bản chuẩn tắc.

Từ vựng#

Những thuật ngữ sau được dùng nhất quán trong mọi tài liệu chuẩn tắc. Tên thuật ngữ giữ nguyên tiếng Anh, kèm cách gọi tiếng Việt tương ứng:

Thuật ngữ Nghĩa
context layer (lớp ngữ cảnh) Chính là artifact mà bản đặc tả này chi phối: một tập tài liệu người đọc được và artifact máy đọc được, thuộc về kho mã nguồn, có đánh phiên bản, mã hoá phần ngữ cảnh vận hành bền vững của một nhóm. “Leji context layer” là dạng đầy đủ dùng khi cần phân biệt rõ. Luôn viết “context layer”; chữ “layer” đứng một mình chỉ dành cho việc gọi tên một thực thể đếm được trong federation (một lớp ngữ cảnh ngang hàng, chủ, đã mount, bị giới hạn, đồng hành, hoặc không truy cập được).
agent Một hệ thống AI biết hành động: nó nạp ngữ cảnh của kho mã nguồn, thực hiện hoặc hỗ trợ công việc, và có thể đề xuất thay đổi. Đây là danh từ chuẩn tắc chỉ chủ thể hành động.
person / people (người) Những người tham gia là con người. Người là bên nắm quyền phê duyệt.
participant (bên tham gia) Một người hoặc một agent.
audience (nhóm đối tượng) Những người và agent được quyền đọc một lớp ngữ cảnh, theo phân quyền của kho mã nguồn cùng mọi phân quyền hệ tệp hay ổ đĩa dùng chung để lộ bản checkout đó. Câu “ai cũng đọc được” chỉ áp dụng trong phạm vi nhóm đối tượng của một lớp ngữ cảnh; các nhóm đối tượng khác nhau được phục vụ bằng những lớp ngữ cảnh riêng, không bao giờ bằng cách chặn nội dung bên trong một lớp.
agent host Sản phẩm hoặc runtime mà agent hoạt động thông qua đó (ví dụ Claude Code, Codex, Cursor). Vendor adapter là thứ cấu hình cho agent host.
tool (công cụ) Một năng lực gọi được mà agent sử dụng (shell, tìm kiếm, một MCP server). Không bao giờ là tên một sản phẩm.
vendor adapter Một tệp điểm vào của agent host, chỉ chuyển hướng tới boot profile và không bao giờ giữ nội dung chính thống. Một số tệp dùng chung được cho nhiều host (AGENTS.md); số khác chỉ phục vụ một host (CLAUDE.md, .cursor/rules). Quy tắc cho cả hai là như nhau; khác biệt chỉ nằm ở chỗ công cụ sinh ra gì theo mặc định.
boot profile Điểm vào không phụ thuộc agent của lớp ngữ cảnh, dành cho cả người lẫn agent.
agent profile Một tài liệu về cách nạp và tư thế làm việc theo từng vai trò, dành cho agent.
AI Dùng như tính từ (AI-native) và trong tên các luồng người với người, người với AI, người với AI với người. Trong tên luồng, “AI” chỉ những agent hoạt động thông qua một agent host.
model (mô hình) Cỗ máy dự đoán mà một agent chạy trên đó. Mô hình không đọc lớp ngữ cảnh; agent mới đọc. Từ này chỉ xuất hiện ở nơi cần phân biệt cỗ máy với chủ thể hành động (ví dụ việc chọn mô hình, vốn là một cơ chế riêng của từng host).

Tóm gọn toàn bộ thứ bậc trong một dòng: model cung cấp năng lực cho agent; agent hoạt động qua agent host và gọi các tool; lớp ngữ cảnh giao tiếp với agent và host, không bao giờ trực tiếp với model. Bản đặc tả trung lập ở mọi tầng trong chồng này: bất kỳ model nào cung cấp năng lực cho bất kỳ agent nào, hoạt động qua bất kỳ host nào, cũng đều dùng cùng một lớp ngữ cảnh. “LLM” cố ý không nằm trong bộ từ vựng vì nó chỉ một lớp model cụ thể, còn bản đặc tả thì trung lập với model.

Ranh giới phạm vi. Leji 1.0 chi phối agent và những agent host nạp ngữ cảnh của kho mã nguồn. AI không mang tính agent (gợi ý tự động hoàn thành, gợi ý ngay trong dòng, trò chuyện không kèm ngữ cảnh kho mã nguồn) nằm ngoài phạm vi chuẩn tắc, trừ khi nó hoạt động như một phần của agent host có nạp lớp ngữ cảnh.

Các tài liệu chuẩn tắc#

Theo thứ tự đọc:

Tài liệu Định nghĩa
context-layer.md Lớp ngữ cảnh, manifest, gốc ngữ cảnh, quy tắc vendor adapter
content-categories.md Năm danh mục nội dung theo logic và cách tệp index ánh xạ nội dung vào chúng
boot-profile.md Điểm vào không phụ thuộc agent mà mọi agent host đều nạp
machine-readable-surface.md Manifest, index, changelog, profile, bản ghi quyết định
decisions.md Bản ghi quyết định
governance.md Đề xuất và phê duyệt, quyền sở hữu, việc đưa vào và loại bỏ, độ tươi
distribution.md Monorepo, submodule nhiều kho mã nguồn, federation
conformance.md Bốn mức tuân thủ và danh sách kiểm
versioning.md Đánh phiên bản cho đặc tả và schema

Các JSON Schema trong ../schemas/ là chuẩn tắc đối với các artifact máy đọc được. Các tài liệu trong ../rationale/../adoption/ không mang tính chuẩn tắc.

Phạm vi của 1.0#

Trong phạm vi: cung cấp ngữ cảnh, đặt ra ràng buộc, ghi lại quyết định, xem xét thay đổi, và đúc kết những khuôn mẫu dùng lại được; cách kết nối không phụ thuộc agent và các vendor adapter (ở mức nhẹ); ngữ nghĩa về quyền sở hữu và tính liên tục (ở mức nhẹ).

Ranh giới mở rộng. Leji 1.0 đặc tả lớp ngữ cảnh dùng chung chính thống: cách ngữ cảnh của một nhóm được viết, sở hữu, đánh phiên bản, đề xuất, phê duyệt, lập index và đọc. Nó cố ý không đặc tả những giao thức thực thi vận hành xung quanh lớp ngữ cảnh đó: bao tác vụ, một giao thức bằng chứng tổng quát, việc bàn giao giữa agent với agent, giao thức phân quyền công cụ, và việc điều phối. Đó là những giao thức mở rộng, không phải điều kiện tiên quyết: một lớp ngữ cảnh tuân thủ 1.0 MUST vẫn hữu ích khi không có chúng, và một bản hiện thực MUST NOT đòi hỏi chúng để đọc, đề xuất, xem xét, phê duyệt hay kiểm tra lớp ngữ cảnh. Chúng sẽ hoàn thiện ngôn ngữ này khi thực tiễn sống chứng minh được, chứ không phải thứ được nghĩ ra một cách trừu tượng.

Leji không phải một ngôn ngữ lập trình, một DSL, một runtime, hay một dịch vụ SaaS. Nó là các quy ước markdown, vài JSON schema nhỏ, và ngữ nghĩa quản trị.

Lớp ngữ cảnh ↗pull request

Lớp ngữ cảnh

Một lớp ngữ cảnh Leji là tập hợp các tài liệu người đọc được, có đánh phiên bản và được quản trị, ghi lại cách một nhóm tư duy về công việc: ngôn ngữ của lĩnh vực, các bất biến của hệ thống, quy ước, ràng buộc bảo vệ và bản ghi quyết định. Người và agent đều đọc lớp này trong công việc thực tế, đồng thời đề xuất thay đổi qua cùng một cổng xem xét. Lớp ngữ cảnh được quản lý phiên bản để lịch sử, tính cập nhật và việc phê duyệt luôn kiểm chứng được; phần cơ chế nằm ở Yêu cầu bên dưới, còn đối tượng và cách thức tham gia nằm ở Tham gia.

Tham gia#

Việc tham gia một lớp ngữ cảnh dựa trên vai trò, không dựa trên công cụ. Đọc, đề xuất, xem xét và phê duyệt diễn ra qua bất kỳ giao diện nào giữ được ngữ nghĩa xem xét và phê duyệt của kho mã nguồn; hiểu biết trực tiếp về git hay dòng lệnh không phải là điều kiện để tham gia.

  • Ai có quyền truy cập thì đọc. Truy cập ở đây nghĩa là truy cập được trên thực tế qua công cụ thường ngày của nhóm, chứ không phải quyền shell vào kho mã nguồn.
  • Ai cũng đề xuất; người phê duyệt. Một đề xuất là một yêu cầu có chủ đích nhằm thay đổi lớp ngữ cảnh. Nó MAY do một người viết trực tiếp, do một agent sinh ra từ yêu cầu của một người, hoặc do một agent sinh ra từ công việc mà nó quan sát được. Trên thực tế, agent soạn phần lớn các thay đổi ngữ cảnh; phần đóng góp của con người không thể rút gọn được chính là quản trị: nêu ra chủ ý và phê duyệt điều gì trở thành chính thống. Người phê duyệt một thay đổi chịu trách nhiệm về ý nghĩa và hệ quả của nó, chứ không phải về việc đích thân thao tác hệ quản lý phiên bản.
  • Ý nghĩa cho người, bề mặt cho máy. Các tài liệu người đọc được mới là nguồn chuẩn tắc cho ngữ cảnh vận hành của một nhóm. Các tệp máy đọc được (manifest, index, changelog) tồn tại để công cụ định vị, lập index, kiểm tra và đồng bộ ý nghĩa đó; chúng không bao giờ thay thế nó.

Dạng chuẩn tắc của những luồng này, vòng tròn (ai cũng đọc, ai cũng đề xuất, người phê duyệt), được định nghĩa trong governance.md.

Đọc một bản ghi#

Nội dung được quản trị có hai loại, được định nghĩa trong content-categories.md: chủ ý, được duy trì như sự thật ở hiện tại, và bản ghi, là bằng chứng có ngày tháng mà trạng thái về sau thay thế chứ không sửa chữa. Cả hai đều được quản trị ngang nhau; khác biệt nằm ở chỗ người đọc được phép làm gì với thứ mình nạp. Người đọc MUST NOT coi một bản ghi là chủ ý hiện hành: một bản ghi cung cấp thông tin với tư cách bằng chứng đúng trong ranh giới mà nó tự nêu, và một người đọc trình bày các khẳng định của bản ghi như trạng thái hiện tại của sự việc, mà không nói rõ điều đó, là đang tự chế ra một tính cập nhật mà tài liệu vốn không có. Điều này phản chiếu quy tắc chế độ suy giảm bên dưới: trong cả hai trường hợp, nghĩa vụ của người đọc là biết, và nói ra, mình đang cầm trong tay thứ có tính cập nhật ở mức nào.

Yêu cầu#

  1. Lớp ngữ cảnh MUST nằm trong một kho git và MUST được đánh phiên bản cùng với phần công việc mà nó mô tả (cùng kho mã nguồn, hoặc một kho ngữ cảnh riêng được tiêu thụ theo distribution.md). Chính kho git là thứ khiến lịch sử của lớp ngữ cảnh, tính cập nhật của bản checkout, và tính toàn vẹn chỉ thêm mới của changelog trở nên kiểm chứng được; công cụ tuân thủ suy ra cả ba từ đó. Đọc lớp ngữ cảnh mà không có kho mã nguồn đó là một chế độ được hỗ trợ nhưng bị suy giảm, định nghĩa ở Chế độ đọc.

  2. Một kho mã nguồn áp dụng Leji MUST mang một tệp manifest, leji.json, ở gốc kho mã nguồn, hợp lệ theo context-manifest.schema.json. Manifest là điểm vào cho máy: nó khai báo phiên bản đặc tả (khoá tự đặt tên leji), tên lớp ngữ cảnh, gốc ngữ cảnh, đường dẫn boot profile, các ánh xạ danh mục, một tuyên bố mức tuân thủ tuỳ chọn, và quyền sở hữu. Nó MAY mang thêm một map agents gắn các định danh vai trò (ví dụ thought-partner, reviewer) với các tài liệu agent profile: giao thức triệu tập vai trò; map quyết định ai lấp vai trò đó. Map là một danh bạ các vai trò, không phải một thứ tự nạp: một liên kết, kể cả liên kết ở khoá default, không bao giờ khiến một profile được đọc; chỉ mục Nạp của boot profile mới làm việc đó.

  3. Manifest MAY khai báo actor: những bên tham gia có tên, có thể lấp vào các vai trò. Mỗi actor khai báo những vai trò mà nó đủ điều kiện đảm nhận, cùng một khuôn lệnh cho từng vai trò. Việc lấy vai trò làm khoá cho câu lệnh chính là điểm mấu chốt: cùng một actor có thể cần một cách gọi khác nhau tuỳ theo nó đang lấp vai trò nào, nên một câu lệnh duy nhất cho mỗi actor không diễn đạt được điều đó. Tập vai trò khai báo của một actor và tập khoá câu lệnh của nó MUST trùng nhau. Ở nơi một vai trò đã có actor, agent profile gắn với vai trò đó MUST NOT khai báo thêm invocation: hai câu lệnh cùng có thẩm quyền mà không nói rõ cái nào thắng là một mâu thuẫn, và lớp ngữ cảnh giải quyết bằng cách chỉ khai báo câu lệnh ở một chỗ. Actor là tuỳ chọn và phần lớn lớp ngữ cảnh không cần tới. Chúng chỉ xứng đáng có mặt khi một vai trò có nhiều hơn một actor đủ điều kiện, hoặc khi một actor cần cách gọi khác nhau tuỳ vai trò nó đang lấp; chỉ một trong hai điều đó cũng đã đủ lý do, còn một vai trò chỉ có một actor và chỉ cần một câu lệnh thì đã được phục vụ bằng chính hostinvocation của profile. Khai báo một actor không trao thẩm quyền nào: nó nói ai có thể được nhờ lấp một vai trò, không bao giờ nói ai được phê duyệt.

    Khuôn lệnh, ở bất cứ đâu chúng xuất hiện (các giá trị commands của một actor và invocation.command của một agent profile), đều theo một quy tắc. Một khuôn là một dòng lệnh dành cho shell mà bên gọi chọn; những cách triệu tập không có hình dạng shell (một lời gọi argv có cấu trúc, một lần spawn trong cùng tiến trình) không biểu diễn được bằng các trường này ở nhánh 1.0. Mọi khuôn MUST mang chỗ giữ <prompt>, và mỗi lần xuất hiện MUST đứng thành một từ shell không trích dẫn của riêng nó ở vị trí đối số, không bao giờ nằm trong dấu trích dẫn hay dính liền với văn bản khác. Việc thay thế diễn ra một lượt: những lần xuất hiện có trong khuôn được viết ra sẽ được thay đồng thời, đúng một lần, nên chuỗi ký tự <prompt> nằm bên trong nội dung prompt vẫn là dữ liệu và không bao giờ bị bung ra lần nữa. Bên gọi sở hữu khâu chuyển giao, và hợp đồng ở đây là kết quả bắt buộc chứ không phải thuật toán trích dẫn: mỗi lần xuất hiện sinh ra đúng một đối số có giá trị bằng đúng nội dung prompt, và không phần nào của nó bị diễn giải như cú pháp shell. Cái mà schema kiểm được chỉ là sự hiện diện của một chỗ để thay thế; còn vị trí và cách chuyển giao là điều quy tắc này bắt buộc và bên gọi phải tôn trọng.

  4. Manifest MUST khai báo một gốc ngữ cảnh (rootPath). Mặc định RECOMMENDEDdocs/. Mọi đường dẫn của lớp ngữ cảnh đều theo kiểu POSIX, tính tương đối từ gốc kho mã nguồn. rootPath khai báo nơi lớp ngữ cảnh nằm; nó không đặt lại gốc cho các đường dẫn mà nó quản trị: các mục index, các trang được ghim, các đường dẫn tới profile và mọi đường dẫn khác trong mọi artifact của Leji đều được giải từ gốc kho mã nguồn, kể cả những đường dẫn có lặp lại tiền tố rootPath. homepage, logofavicon của viewer là ngoại lệ: chúng được viết tương đối so với gốc ngữ cảnh, và một đường dẫn tương đối so với gốc kho mã nguồn nhưng nằm dưới gốc ngữ cảnh cũng được chấp nhận. Đường dẫn của danh mục và của machine SHOULD nằm dưới rootPath; bộ kiểm tra sẽ cảnh báo khi không phải vậy.

  5. Lớp ngữ cảnh MUST có một boot profile theo boot-profile.md. Vị trí mặc định RECOMMENDEDdocs/boot-profile.md; bootProfilePath trong manifest khai báo vị trí thực tế.

  6. Nội dung của lớp ngữ cảnh MUST ưu tiên người đọc được. Markdown là định dạng RECOMMENDED cho văn xuôi; siêu dữ liệu có cấu trúc dùng frontmatter YAML hoặc các artifact JSON được định nghĩa trong machine-readable-surface.md. Một tài liệu chỉ máy mới đọc được thì không thuộc về lớp ngữ cảnh.

  7. Lớp ngữ cảnh MUST có một người sở hữu được nêu tên (owners.primary trong manifest): một người chịu trách nhiệm về tính cập nhật của nó. Lớp ngữ cảnh vô chủ thì sẽ mục ruỗng.

Chế độ đọc: chính thống và suy giảm#

Một lớp ngữ cảnh được đọc theo hai chế độ; người đọc MUST biết mình đang ở chế độ nào, vì các bảo đảm là khác nhau. Người đọc xác định chế độ của mình từ những gì nó giải được: một leji.json tới được ở gốc kho mã nguồn cùng với hoặc một cây làm việc git hoặc danh tính phiên bản kho mã nguồn do nền tảng chủ quản cung cấp, là chính thống; nội dung tới được dưới dạng tệp thuần mà không có cả hai thứ đó là suy giảm.

  1. Chính thống. Người đọc giải được lớp ngữ cảnh qua chính kho git của nó: một bản checkout, hoặc khung nhìn kho mã nguồn của nền tảng chủ quản. Lịch sử, tính cập nhật của bản checkout và tính toàn vẹn của changelog đều kiểm chứng được, và nội dung đã phê duyệt được biết là còn hiệu lực tính tới phiên bản đang đọc.
  2. Suy giảm. Người đọc tiếp cận lớp ngữ cảnh như nội dung tệp thuần, không có cây làm việc git hay siêu dữ liệu phiên bản nào truy cập được: tệp được tải lên, được đồng bộ, hoặc được sao chép sang một giao diện khác mà không kèm kho mã nguồn. Việc đọc tệp thuần là hạng nhất về mặt đọc (tài liệu vốn đã bắt buộc phải người đọc được, và changelog máy đọc được vẫn truyền tải được mức độ mới đã khai báo), nhưng một người đọc ở chế độ suy giảm MUST coi tính cập nhật của bản checkout và trạng thái phê duyệt là không rõ, không bao giờ coi là còn hiệu lực (xem governance.md, phần Độ tươi). Changelog là bề mặt mang tính di động cho mức độ mới đã khai báo trong chế độ này; tự nó không chứng minh được rằng bản sao khớp với kho mã nguồn chính thống.

Đọc ở chế độ suy giảm mở rộng phạm vi ai và cái gì có thể tiêu thụ một lớp ngữ cảnh; nó không bao giờ là một con đường dẫn tới thẩm quyền chính thống. Một thay đổi chỉ trở thành chính thống qua cổng xem xét dựa trên git, và một bản sao suy giảm không thể thoả mãn các phép kiểm ở chế độ chính thống mà federation phụ thuộc vào (tính cập nhật của pin, trạng thái pin lỗi thời, sự nguyên vẹn của quyền sở hữu, và quyền truy cập mount bị giới hạn theo distribution.md).

Quy tắc vendor adapter#

Các tệp cấu hình của agent host (ví dụ CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, .cursor/rules, .windsurfrules, .github/copilot-instructions.md):

  1. MUST NOT giữ nội dung chính thống của lớp ngữ cảnh.
  2. Nếu có mặt, MUST chuyển hướng tới boot profile (thường là một con trỏ một dòng).
  3. MAY mang những cơ chế riêng của host mà ngoài agent host đó ra thì không có nghĩa gì (chọn mô hình, thiết lập trình chạy), miễn là không có tri thức nào của nhóm nằm ở đó.
  4. Công cụ tìm ra cần kiểm tra những điểm vào nào từ hai nguồn: danh sách vendorAdapters tuỳ chọn trong manifest, và một tập well-known đã công bố (chính các tệp nêu trên). Danh sách ví dụ ở trên chính là tập well-known của nhánh này; một host có điểm vào không nằm trong đó chỉ được kiểm tra khi manifest nêu tên nó trong vendorAdapters.

Các quy ước điểm vào hiện có chỉ cho agent host biết phải tìm ở đâu; Leji định nghĩa agent sẽ thấy gì ở đó. Mọi bên tham gia cùng đọc một nguồn sự thật duy nhất.

Lớp ngữ cảnh không phải là gì (tham khảo)#

  • Không phải một wiki. Không có gì buộc wiki phải luôn cập nhật. Lớp ngữ cảnh duy trì được sức sống vì agent đọc nó trong mọi tác vụ (ngữ cảnh sai tạo ra đầu ra sai và bị nhận ra ngay), vì các thay đổi đi qua vòng xem xét như code và vì công cụ giúp nhận biết nội dung lỗi thời.
  • Không phải tài liệu theo nghĩa truyền thống. Tài liệu truyền thống mô tả hệ thống làm gì sau khi công việc đã hoàn tất. Lớp ngữ cảnh mô tả cách nhóm suy nghĩ ở hiện tại và được cả người lẫn agent đọc liên tục.
  • Không phải một mẫu để nhập về. Ngữ cảnh vay mượn sẽ lỗi thời ngay lập tức. Giá trị của lớp ngữ cảnh nằm ở chỗ nó phản ánh chính nhóm đó; Leji chuẩn hoá hình thái và cách quản trị, chứ không chuẩn hoá nội dung.

Danh mục nội dung ↗pull request

Danh mục nội dung

Leji định nghĩa năm danh mục nội dung theo logic. Các danh mục phân loại tài liệu theo mục đích sử dụng, không theo nơi lưu trữ: tên danh mục là những định danh ổn định dùng chung cho manifest, index và công cụ. Còn tên thư mục do nhóm tự quyết định.

Năm danh mục#

Danh mục Những gì thuộc về nó
domain Ngôn ngữ nghiệp vụ và ngữ nghĩa sản phẩm, bằng chính lời của nhóm: những danh từ cốt lõi nghĩa là gì, chúng liên hệ với nhau ra sao, những thuật ngữ có nghĩa riêng ở đây. Bản ghi về trạng thái nghiệp vụ (tình trạng một hợp đồng, một lát cắt thị trường) cũng thuộc về đây, dưới dạng bản ghi.
system Kiến trúc và các bất biến của nó: ranh giới dịch vụ, quyền sở hữu dữ liệu, hợp đồng tích hợp, mô hình nhất quán, hợp đồng khi lỗi, những ràng buộc mà mọi thay đổi đều phải sống chung. Các đánh giá kỹ thuật và báo cáo hệ thống thuộc về đây, dưới dạng bản ghi.
practice Những quy ước và khuôn mẫu được áp dụng một cách tự động: quy ước viết code, khuôn mẫu kiểm thử, cùng những khuôn mẫu về prompt và quy trình đã được chứng minh (xem cổng đúc kết bên dưới). Bản ghi về việc áp dụng một phương pháp (một buổi retro, nhật ký chạy một runbook) thuộc về đây, dưới dạng bản ghi.
governance Các ràng buộc bảo vệ cho agent và các quy tắc vận hành: agent được làm gì mà không cần hỏi, việc gì cần một cổng chặn có người, quy tắc xử lý dữ liệu, điều kiện leo thang, các kiểm soát tuân thủ. Bằng chứng quản trị (nhật ký kiểm toán, một báo cáo xem xét) thuộc về đây, dưới dạng bản ghi.
decisions Bản ghi có ngày tháng về lý do mọi thứ lại như hiện nay, theo decisions.md.

Chủ ý và bản ghi#

Mọi tài liệu được quản trị đều hoặc là chủ ý hoặc là một bản ghi, độc lập với danh mục của nó:

  • Chủ ý là sự thật ở hiện tại được duy trì: bảng thuật ngữ, bất biến, quy ước, ràng buộc bảo vệ. Người đọc dựa vào nó như thứ còn hiệu lực, nên khi thực tế đổi thay, tài liệu được sửa lại. Chủ ý chính là thứ mà hạn xem xét lại và cơ chế độ tươi sinh ra để phục vụ (xem governance.md).
  • Một bản ghi giữ lại các khẳng định trong một ranh giới thời gian hoặc sự kiện được nêu rõ: trạng thái, đánh giá, sổ ghi, báo cáo, kết quả cuộc họp, lưu trữ. Trạng thái về sau thay thế một bản ghi chứ không sửa nó; bản gốc vẫn là một tường thuật đúng về thời điểm của nó. Bề mặt thể hiện tính cập nhật của một bản ghi là ngày tháng của nó, không bao giờ là một hạn xem xét lại.

Để phân loại, hãy trả lời một câu hỏi: nếu thông tin sau này mâu thuẫn với tài liệu, ta phải sửa tài liệu vì người đọc coi nó là thông tin hiện hành, hay thông tin mới sẽ thay thế nó còn bản gốc vẫn là ghi chép đúng về thời điểm trước đó? Nếu phải sửa, đó là chủ ý; nếu bị thay thế, đó là bản ghi.

Một bản ghi được quản trị y hệt như chủ ý: được lập index, được xem xét, có chủ sở hữu, và được định tuyến. Khác biệt nằm ở chỗ người đọc được phép làm gì với nó: người đọc MUST NOT coi một bản ghi là chủ ý hiện hành; nó là bằng chứng có ngày tháng (xem context-layer.md, phần Đọc một bản ghi). Bản ghi quyết định là dạng bản ghi chính thức: tự thân chúng là bản ghi, với schema và vòng đời riêng theo decisions.md.

Một số câu hỏi về bản ghi cố ý nằm ngoài 1.0 và được thừa nhận thay vì giấu đi: không có khái niệm máy hiểu được về một chuỗi bản ghi (nên công cụ không bao giờ chứng nhận bản ghi nào là “mới nhất”), không có cơ chế theo dõi độ mới của một dòng bản ghi (liệu bản ghi kế tiếp đã quá hạn chưa), và không có phân loại ở mức từng mục cho những tài liệu trộn lẫn đáng kể cả chủ ý lẫn nội dung bản ghi. Một tài liệu trộn lẫn SHOULD được tách ra; ở nơi việc tách là quá tốn kém, hãy phân loại theo hợp đồng mà người đọc phía sau chủ yếu dựa vào. Nội dung thành thật mà nói là không hợp danh mục nào thì cứ để làm tài liệu tham khảo; việc phân loại không hứa hẹn là sẽ khỏi cần phán đoán.

Yêu cầu#

  1. Manifest MUST ánh xạ mỗi danh mục mà nó tuyên bố tới một hoặc nhiều tệp index có đường dẫn tương đối từ gốc kho mã nguồn (categories.<id>.indexes); mỗi tệp index SHOULD nằm dưới gốc ngữ cảnh đã khai báo, theo context-layer.md. Một tệp index khai báo việc đưa vào, chứ không di dời: nội dung vẫn nằm ở nơi nhóm vốn để nó (ví dụ business/, technology/, architecture/), và một thư mục có thể đóng góp tài liệu cho nhiều danh mục mà không phải đổi tên gì cả.
  2. Một tệp index là markdown được biên tập có mang một hoặc nhiều khối mã rào leji-index. Một khối mở bằng một dòng có từ ba dấu backtick trở lên, theo sau là chuỗi thông tin của khối, và đóng bằng dòng kế tiếp có từ ba dấu backtick trở lên; số backtick của rào đóng không nhất thiết khớp với rào mở. Chỉ đúng ba chuỗi thông tin là hợp lệ: leji-index (một khối chủ ý), leji-index intent (cũng thế, nói rõ ra), và leji-index record (một khối bản ghi, các mục trong đó được giải thành bản ghi). Bất kỳ token nào khác sau leji-index đều là lỗi phân tích cú pháp, không bao giờ bị âm thầm bỏ qua: văn phạm này hữu hạn theo thiết kế. Mỗi khối liệt kê nội dung mỗi dòng một mục theo dạng - path: <đường-dẫn-tương-đối-từ-gốc-kho-mã>, trong đó một đường dẫn là một thư mục (markdown bên trong được đưa vào theo cách đệ quy) hoặc một tệp markdown đơn lẻ. Một đường dẫn MUST là POSIX tương đối từ gốc kho mã nguồn: một dấu / mở đầu, một đoạn .., hay một dấu gạch chéo ngược đều không hợp lệ và bị từ chối. Dòng trống và dòng chú thích # chiếm trọn dòng đều được bỏ qua, và một mục MAY mang một # chú thích ở cuối dòng, phía trước có khoảng trắng. Khoảng trắng trong văn phạm này là dấu cách ASCII (U+0020) và tab (U+0009), không gì khác, ở mọi nơi văn phạm cần tới nó: quanh các backtick của rào và chuỗi thông tin, làm phần đệm đầu và cuối của một dòng mục, và ngay trước dấu # mở một chú thích cuối dòng. Một byte order mark UTF-8 ở đầu tệp được cắt bỏ trước khi phân tích. Các dòng được tách theo LF, chấp nhận có CR ở cuối, và tệp là UTF-8. Các bản hiện thực MUST NOT dùng lớp khoảng trắng của runtime ở đây: mọi ký tự khác mà một runtime tình cờ xếp vào loại khoảng trắng, trong đó có U+0085 và U+00A0, đều là nội dung đường dẫn bình thường, nên một mục có đường dẫn chứa ký tự như vậy sẽ bị báo là không tìm thấy chứ không bị âm thầm cắt bỏ. Các khối leji-mounts của boot-profile.md được đóng băng trên cùng bảng chữ cái đó, nên một bộ quét đọc được cả hai văn phạm và ba bản hiện thực không thể bất đồng về chuyện một rào có tồn tại hay không. Nhiều khối trong cùng một tệp được nối lại theo thứ tự trong tài liệu. Văn xuôi và tiêu đề quanh các khối đều được phép, nên một tệp index kiêm luôn vai trò một tấm bản đồ người đọc được của danh mục. Việc quét dựa trên từng dòng và không xét tới cấu trúc markdown: một dòng mang từ ba backtick trở lên cùng với tag, sau phần thụt đầu dòng tuỳ chọn bằng dấu cách hay tab, sẽ mở một khối thật ở bất cứ chỗ nào nó nằm trong tài liệu, kể cả bên trong một ví dụ rào dài hơn hay bên trong một mục danh sách. Do đó, một ví dụ chỉ để minh hoạ chứ không để khai báo phải được rào bằng một tag khác, không bao giờ bằng cách thêm một token sau leji-index: tag mới là thứ bộ quét đối sánh, nên leji-index example sẽ mở một khối thật và báo lỗi phân tích cú pháp, còn một rào gắn tag text thì không mở gì cả. Vị trí RECOMMENDEDcontext/<id>.md dưới gốc ngữ cảnh; vị trí này cấu hình được và công cụ không bao giờ ghi cứng nó.
  3. Một lớp ngữ cảnh MUST ánh xạ ít nhất domain hoặc system, cộng thêm decisions, để tuyên bố được bất kỳ mức tuân thủ nào (xem conformance.md), và phần tối thiểu domain/system có nội dung MUST gồm ít nhất một tài liệu chủ ý: một lớp ngữ cảnh chỉ toàn bản ghi thì giữ được lịch sử nhưng không mang theo ngữ cảnh vận hành nào. Các danh mục còn lại bồi đắp dần khi nhóm gặp câu hỏi thật; một danh mục rỗng (danh mục mà tệp index của nó không giải ra tài liệu nào) MUST NOT được ánh xạ chỉ để cho đủ một danh sách kiểm.
  4. Một tài liệu được giải về đúng một danh mục và một loại. Các mục index là bộ chọn, và việc giải tuân theo độ đặc hiệu của bộ chọn: một bộ chọn trỏ thẳng vào tệp thắng mọi bộ chọn thư mục, và một bộ chọn thư mục sâu hơn thắng một bộ chọn thư mục tổ tiên. Bộ chọn đặc hiệu nhất bao phủ một tài liệu sẽ quyết định danh mục và loại khối của tài liệu đó; một tài liệu mà một bộ chọn rộng hơn có bao phủ nhưng một bộ chọn đặc hiệu hơn thắng thì đơn giản là không thuộc nội dung của bộ chọn rộng kia (đó là cách diễn đạt một tệp luôn được cập nhật nằm trong một thư mục bản ghi, hay nhật ký quyết định của một nhóm nằm trong một cây đã ánh xạ rộng hơn, mà không phải di chuyển gì cả). Những bộ chọn ngang độ đặc hiệu mà bất đồng về danh mục hoặc loại là một lỗi, không bao giờ được giải theo thứ tự index; những gán ngang độ đặc hiệu mà giống hệt nhau thì chỉ giải một lần, còn một mục bị lặp y nguyên trong cùng một tệp index thì bị từ chối. Công cụ SHOULD làm nổi lên bộ chọn nào mà mọi tài liệu nó bao phủ đều bị các bộ chọn đặc hiệu hơn giành mất (một bộ chọn bị che): đó là phần thừa trong tấm bản đồ được biên tập, không bao giờ là một lỗi. Ngoài ra, việc giải là tất định: một mục thư mục nở ra thành các tệp markdown của nó theo thứ tự từ điển POSIX (theo Unicode code point; RECOMMENDED giữ đường dẫn ở ASCII để thứ tự không mơ hồ giữa các bản hiện thực), và mọi đường dẫn có vị trí thật (sau khi giải symlink) thoát ra ngoài gốc kho mã nguồn đều bị loại trừ chứ không đi theo. Các mục index (xem machine-readable-surface.md) mang theo định danh danh mục và loại.
  5. Một tài liệu MAY khai báo loại của mình trong frontmatter (kind: intent hoặc kind: record); frontmatter ghi đè loại khối của bộ chọn thắng cuộc và không bao giờ ghi đè danh mục. Mọi giá trị kind khác đều là lỗi. Bản ghi quyết định không nhận khoá kind (schema của chúng là đóng và tự thân chúng đã là bản ghi). Một bản ghi MAY mang một date trong frontmatter (YYYY-MM-DD); công cụ đọc ngày của một bản ghi chỉ từ trường đó, không bao giờ từ văn xuôi, quy ước tiêu đề hay tên tệp. Một bản ghi MUST NOT mang freshness.reviewAfter (hạn xem xét lại là một cơ chế của chủ ý; đặt trên một bản ghi, nó hứa hẹn một tính cập nhật mà tài liệu không thể có, và đó là một lỗi).
  6. Nội dung practice mô tả các khuôn mẫu prompt hay quy trình SHOULD chỉ được đúc kết sau khi khuôn mẫu đó đã hiệu quả ít nhất hai lần (cổng chứng minh qua hai lần). Đúc kết quá sớm chính là cách các thư mục practice đầy lên bằng nguyện vọng.

Ghi chú (tham khảo)#

Không phải danh mục nào cũng cần có ngay từ ngày đầu. Lớp ngữ cảnh tối thiểu khả dụng chỉ gồm những gì công việc trong tháng đầu thực sự cần đến. Nhờ các danh mục, người hoặc agent có thể hỏi “đây là loại sự thật nào?” rồi nạp đúng lát cắt cần cho tác vụ trước mắt, thay vì nạp cả cây.

Hai loại này tồn tại vì tài liệu của một kho mã nguồn thật vốn là hai kho ngữ liệu đan xen với hai mô hình sự thật khác nhau, và ép nửa vận hành phải theo ngữ nghĩa của chủ ý thì hỏng cả hai đường: hoặc là những lời hứa về độ tươi không thể giữ, hoặc là phần lớn kho mã nguồn bị đẩy ra ngoài vòng quản trị. Một hình hài đã dùng thật, với một ngoại lệ chủ ý nằm bên trong một thư mục bản ghi:

# Ngữ cảnh lĩnh vực

```leji-index
- path: docs/glossary.md
```

Trạng thái vận hành được quản trị như bản ghi; chính sách leo thang vẫn là chủ ý.

```leji-index record
- path: docs/operations/
```

```leji-index intent
- path: docs/operations/escalation-policy.md
```

Boot profile ↗pull request

Boot profile

Boot profile là điểm vào không phụ thuộc agent của lớp ngữ cảnh: một tài liệu duy nhất, người đọc được, để mọi agent host và mọi người cùng bắt đầu. Tài liệu này trả lời ba câu hỏi: “lớp ngữ cảnh này là gì, tôi phải nạp những gì, và tôi phải hành xử thế nào ở đây?”.

Yêu cầu#

  1. Lớp ngữ cảnh MUST có đúng một boot profile, nằm ở đường dẫn mà bootProfilePath trong manifest khai báo. Mặc định RECOMMENDEDdocs/boot-profile.md.

  2. Boot profile MUST là markdown thuần, một người không cần công cụ gì cũng đọc được. Nó MUST NOT phụ thuộc vào cú pháp cấu hình của bất kỳ nhà cung cấp nào.

  3. Boot profile MUST bao gồm:

    • Danh tính: một đoạn văn cho biết kho mã nguồn hay sản phẩm này là gì.
    • Nạp: cần đọc ngữ cảnh nào cho loại tác vụ nào. Phần này MUST đưa ra một tập vô điều kiện (đọc gì trước mọi tác vụ), rồi tới các bộ chọn theo loại tác vụ, định tuyến theo đường dẫn, theo danh mục, hoặc qua context index, cùng một phương án dự phòng đã định nghĩa cho tác vụ không khớp bộ chọn nào. Diễn đạt bằng ngôn ngữ tác vụ, đây chính là cách thuật toán Định tuyến tác vụ (machine-readable-surface.md) thể hiện ở tầng boot profile; đi theo nó không đòi hỏi phải biết gì về thuật toán đó.
    • Tư thế: những kỳ vọng vận hành đặt lên agent (khi nào cứ làm, khi nào phải hỏi, việc gì không bao giờ được làm). Phần này MAY được mang theo bằng cách dẫn chiếu tới nội dung governance hoặc tới một agent profile cốt lõi.
  4. Boot profile SHOULD liên kết tới manifest, tới index (nếu có), và tới các agent profile (nếu có), để một agent bước vào qua bất kỳ host nào cũng khám phá được toàn bộ bề mặt máy đọc được.

  5. Boot profile MUST nói bằng ngôn ngữ tác vụ: nó nêu tên các đường dẫn cụ thể và một thứ tự nạp rõ ràng, và đi theo nó không đòi hỏi phải biết gì về bản đặc tả này. Manifest và schema tồn tại cho công cụ, không phải cho agent; một boot profile đòi người đọc phải rành đặc tả mới theo được là một dấu hiệu xấu về mức tuân thủ.

  6. Boot profile SHOULD nêu rõ các nghĩa vụ bảo trì của lớp ngữ cảnh: thay đổi của nó được ghi ở đâu (changelog đã khai báo) và quyết định được ghi lại ra sao (vị trí bản ghi quyết định đã khai báo). Bộ kiểm tra sẽ cảnh báo khi boot profile không nhắc tới cái nào trong hai thứ đó.

  7. Các tệp điểm vào của nhà cung cấp chuyển hướng tới boot profile theo quy tắc vendor adapter trong context-layer.md.

  8. Tập nạp vô điều kiện của boot profile (những gì nó bảo phải đọc trước mọi tác vụ) SHOULD được giới hạn trong đúng phần mà mọi tác vụ đều cần. Ngữ cảnh mà chỉ một số tác vụ cần SHOULD được định tuyến theo tác vụ, theo danh mục, hoặc qua index, thay vì nạp sẵn; và bản ghi quyết định SHOULD được định tuyến theo affectedPaths / affectedCategories mà chúng khai báo, thay vì nạp cả thư mục, vì chúng tích tụ vô hạn. Mọi thứ trong tập vô điều kiện đều bị trả giá ở mọi tác vụ.

  9. Các lớp ngang hàng trong federation. Một lớp ngữ cảnh có khai báo federation.mounts (theo distribution.md) MUST làm nổi lên những lớp ngang hàng đó trong boot profile dưới một dạng máy kiểm được: một hoặc nhiều khối rào có chuỗi thông tin là leji-mounts, đặt ở bất cứ đâu trong tài liệu, các mục của chúng nối lại theo thứ tự trong tài liệu và mang đúng một mục cho mỗi mount đã khai báo. Một mục nêu tên lớp ngang hàng, chủ sở hữu của nó, nó chứa gì, và khi nào cần đọc nó, hai điều sau viết bằng ngôn ngữ tác vụ của chính người soạn. Ví dụ đầy đủ nằm bên dưới phần yêu cầu.

    Văn phạm được cố định để mọi bản hiện thực đọc giống hệt nhau. Một khối mở bằng một dòng có từ ba dấu backtick trở lên, theo sau là chuỗi thông tin, và đóng bằng dòng kế tiếp có từ ba dấu backtick trở lên; số backtick của rào đóng không nhất thiết khớp với rào mở. Chuỗi thông tin chỉ gồm leji-mounts; một rào mang thêm token nào sau đó là một lỗi, không bao giờ là một rào bị bỏ qua. Các dòng rào MAY mang phần thụt đầu dòng và phần đệm bằng dấu cách hoặc tab, còn các bản ghi nằm giữa chúng thì MUST NOT: một bản ghi bắt đầu ở cột 1 với - mount: , và các trường của nó thụt vào đúng hai dấu cách ASCII. Trong một bản ghi, owner, carriesread-when mỗi trường xuất hiện đúng một lần, theo thứ tự bất kỳ; trường lạ, trường trùng lặp và trường thiếu đều là lỗi. Một giá trị là phần còn lại không rỗng của dòng sau tiền tố key: , không có dấu cách hay tab ở đầu và cuối, và không chứa ký tự điều khiển hay ký tự phân tách dòng. Khoảng trắng trong văn phạm này là dấu cách ASCII (U+0020) và tab (U+0009), không gì khác, trong phần thụt và phần đệm của dòng rào cũng như trong một dòng nội dung; các bản hiện thực MUST NOT dùng lớp khoảng trắng của runtime ở đây, vì chúng bất đồng về những ký tự như U+0085 và U+00A0 và sẽ bất đồng về chuyện một khối có tồn tại hay không. Một byte order mark UTF-8 ở đầu tệp được cắt bỏ trước khi phân tích. Các dòng được tách theo LF, chấp nhận có CR ở cuối, dòng trống và dòng bắt đầu bằng # chiếm trọn dòng đều được bỏ qua (như trong các khối index danh mục ở content-categories.md), và tệp là UTF-8. Việc quét dựa trên từng dòng và không xét tới cấu trúc markdown: một dòng mang từ ba backtick trở lên cùng với tag, sau phần thụt đầu dòng tuỳ chọn bằng dấu cách hay tab, sẽ mở một khối thật ở bất cứ chỗ nào nó nằm trong tài liệu, kể cả bên trong một ví dụ rào dài hơn hay bên trong một mục danh sách. Do đó, một ví dụ chỉ để minh hoạ chứ không để khai báo phải được rào bằng một tag khác, không bao giờ bằng cách thêm một token sau leji-mounts: tag mới là thứ bộ quét đối sánh, nên leji-mounts example sẽ mở một khối thật và báo lỗi phân tích cú pháp, còn một rào gắn tag text thì không mở gì cả. mount MUST khớp với name của một mount đã khai báo và owner MUST khớp với owner.name đã khai báo của mount đó, so sánh dưới dạng chuỗi đã giải mã; một mục cho một mount chưa khai báo, một mục thứ hai cho cùng một mount, và một mount đã khai báo mà không có mục nào, tất cả đều là lỗi. Một lớp ngữ cảnh không khai báo mount nào MUST NOT mang khối leji-mounts.

    Vị trí của lớp ngang hàng cố ý không phải một thành phần trong đó: một mount được hiện thực hoá trong một phép chiếu cục bộ trên máy, định địa chỉ theo nội dung, nên người đọc giải nó bằng leji mounts locate <name> chứ không suy ra một đường dẫn (theo distribution.md). Văn xuôi quanh khối SHOULD giải thích cách định tuyến một cách tự nhiên; khối là phần lõi kiểm được, không bao giờ thay thế cho phần văn xuôi ấy hay cho phần khai báo trong leji.json. Các lớp ngang hàng đã mount là những nguồn riêng biệt, có tên, không bao giờ bị trộn vào các danh mục của lớp chủ; boot profile chỉ định tuyến agent vào một lớp ngang hàng khi tác vụ khớp với cách định tuyến của nó hoặc khi profile yêu cầu. Phần không được kiểm là cố ý: carriesread-when là văn bản tự do, và mức độ trung thực của chúng với siêu dữ liệu định tuyến của mount là do nhóm tự khai chứ không do công cụ xác minh, vì công cụ chỉ kiểm việc liệt kê, danh tính và sự hiện diện. Việc làm nổi các lớp ngang hàng ở đây giữ cho việc khám phá mount nằm ngay trong điểm vào bằng ngôn ngữ tác vụ của agent, nên đi theo yêu cầu 5 vẫn không cần đọc manifest.

Một khối leji-mounts đầy đủ#

Một mục, cho một lớp chủ khai báo đúng một mount tên acme-product-context. Khối nằm ở cột 1 trong boot profile, đúng như nó hiện ra ở đây; rào bốn backtick bên ngoài là lớp bọc của chính tài liệu này và không thuộc về khối.

```leji-mounts
- mount: acme-product-context
  owner: Product team
  carries: product-side domain language and the decisions behind the customer-facing surface
  read-when: a task touches product behavior, product terminology, or billing
```

Agent profile#

Một lớp ngữ cảnh MAY định nghĩa các profile theo vai trò (ví dụ profile người xem xét, profile phát hành, profile QA) nằm dưới một thư mục do machine.agentProfilesPath khai báo. Mỗi profile:

  1. MUST là markdown kèm frontmatter YAML hợp lệ theo agent-profile.schema.json.

  2. MUST, sau khi đã giải xong phần kế thừa, mang theo những gì vai trò đó đọc trước tiên (requiredRead) và khi nào nó phải dừng lại để hỏi (mustAskWhen). Một profile có khai báo inherits MAY bỏ qua một trong hai nếu profile gốc đã cung cấp; một profile không khai báo inherits thì MUST tự khai báo cả hai.

  3. MAY khai báo inherits, và trường này có hiệu lực trong nhánh 1.0: nó nêu tên đúng một profile khác trong tập profile của lớp ngữ cảnh, profile đó MUSTrolecore, và profile hiện tại mở rộng tư thế lẫn phần thân của nó. Tập profile của lớp ngữ cảnh gồm mọi tài liệu nằm dưới machine.agentProfilesPath đã khai báo, cùng mọi tài liệu được nêu tên trong map agents của manifest, bất kể tài liệu đó nằm ở đâu. Việc giải chỉ đi một tầng, nên một profile có rolecore MUST NOT khai báo inherits, và đích được nêu tên MUST tồn tại, MUST là duy nhất theo id, và bản thân nó MUST NOT khai báo inherits. Việc giải kết hợp như sau:

    • Các mảng tư thế (requiredRead, defaultContext, mustAskWhen, mustRefuseWhen): các mục của profile gốc theo đúng thứ tự đã soạn, rồi tới các mục của profile dẫn xuất theo thứ tự của nó, bỏ đi những mục mà profile gốc đã có. Thứ tự soạn chính là chủ ý về việc nạp, nên không có gì bị sắp xếp lại.
    • Mọi trường còn lại (id, name, role, purpose, version, host, invocation, escalation, owners, freshness): lấy của chính profile dẫn xuất, không bao giờ kế thừa. inherits là một chỉ thị về việc giải và bản thân nó không thuộc về profile đã giải xong.
    • Phần thân: cả hai phần thân đều là chuẩn tắc, phần của profile gốc trước, rồi tới phần của profile dẫn xuất.

    Một bên tiêu thụ không giải được một profile có kế thừa thì MUST NOT tự mình áp dụng tệp dẫn xuất; tệp dẫn xuất chỉ là một nửa của một profile, nên bên tiêu thụ báo là không hỗ trợ thay vì áp dụng. Ở nơi một điều kiện phải hỏi và một điều kiện phải từ chối cùng áp cho một tình huống, thì từ chối thắng.

    Việc giải bảo đảm sự kết hợp, chứ không bảo đảm sự thu hẹp ngữ nghĩa: văn xuôi ở profile dẫn xuất mà mâu thuẫn hoặc nới lỏng profile gốc là không tuân thủ, và không công cụ nào phát hiện được một mâu thuẫn trong ngôn ngữ tự nhiên.

Profile điều chỉnh một vai trò nạp gì và hành xử ra sao; chúng không nhân bản nội dung của lớp ngữ cảnh.

Hai trường tuỳ chọn hostinvocation của một profile là dạng viết tắt cho trường hợp một actor: chúng nói cách triệu tập bên tham gia duy nhất lấp vai trò này. command của nó là một khuôn theo đúng quy tắc của khuôn lệnh dành cho actor, kể cả chỗ giữ <prompt> và cách đặt nó (xem context-layer.md, phần Yêu cầu). Ở nơi một vai trò có nhiều hơn một bên tham gia đủ điều kiện, hoặc nơi cùng một bên tham gia cần cách triệu tập khác nhau tuỳ vai trò đang lấp, thì registry actors tuỳ chọn của manifest mang việc đó thay (cùng mục ấy). Một vai trò dùng cơ chế này hoặc cơ chế kia, không bao giờ dùng cả hai.

Ghi chú (tham khảo)#

Boot profile được chủ ý viết thật đơn giản: đó là tấm bản đồ kèm tư thế làm việc, không phải kho tri thức. Nếu boot profile dài quá vài màn hình, tức là có nội dung đang nằm ở điểm vào trong khi lẽ ra phải thuộc về một danh mục.

Thiết kế này ngăn một kiểu hỏng do quá nhiều tầng gián tiếp: mỗi bước chuyển từ ngữ cảnh đầu tiên của agent đến ràng buộc thực sự đều tiêu tốn thêm sự chú ý. Một lớp ngữ cảnh được thiết kế tốt không cần điểm vào nào của nhà cung cấp (lệnh triệu tập có thể trỏ thẳng tới boot profile), còn boot profile dẫn thẳng tới nội dung. Chiều sâu nên nằm trong các tài liệu của lớp ngữ cảnh, không phải trên con đường dẫn tới chúng.

Mọi tài liệu mà boot profile yêu cầu đọc trước tất cả tác vụ đều tạo chi phí cho từng tác vụ, nên tập vô điều kiện là phần đắt đỏ nhất của lớp ngữ cảnh. Chỉ giữ ở đó những gì thực sự dùng chung; phần còn lại nên được định tuyến theo loại tác vụ, danh mục, index và phạm vi tự khai báo của từng bản ghi quyết định. Index giúp agent nạp đúng lát cắt cần cho tác vụ thay vì cả cây; quyết định thì tích luỹ không giới hạn, nên phải được định tuyến chứ không bao giờ nạp sẵn cả thư mục.

Bề mặt máy đọc được ↗pull request

Bề mặt máy đọc được

Năm artifact giúp công cụ đọc được lớp ngữ cảnh. Phần còn lại là văn xuôi viết cho con người mà agent cũng có thể đọc; chính năm artifact này mới tạo thành hợp đồng để công cụ dựa vào.

Artifact Vị trí mặc định Schema
Manifest leji.json (gốc kho mã nguồn, cố định) context-manifest.schema.json
Context index <root>/context-index.json context-index.schema.json
Context changelog <root>/context-changelog.json context-changelog.schema.json
Agent profile <root>/agents/*.md (frontmatter) agent-profile.schema.json
Bản ghi quyết định <root>/decisions/*.md (frontmatter) decision-record.schema.json

Mọi vị trí trừ manifest đều do manifest khai báo; bảng trên chỉ nêu giá trị mặc định.

Yêu cầu#

  1. Manifest. leji.json MUST tồn tại ở gốc kho mã nguồn và hợp lệ theo schema của nó. Đây là tên tệp cố định duy nhất trong Leji: chính là tệp mà công cụ luôn tìm tới.
  2. Index. Một lớp ngữ cảnh tuyên bố mức tuân thủ indexed trở lên MUST mang một context index được sinh ra, không bao giờ duy trì bằng tay: công cụ giải các tệp index của danh mục (categories.<id>.indexes, theo content-categories.md) thành những tài liệu mà chúng liệt kê, rồi ghi một mục cho mỗi tài liệu được quản trị. Mỗi mục mang một id ổn định, một path, một title, và một định danh category. Bộ sinh SHOULD phát ra thêm kind của tài liệu (intent hoặc record); trường này là tuỳ chọn trong schema để một index viết ra từ trước khi có khái niệm loại vẫn hợp lệ, và bên tiêu thụ coi giá trị vắng mặt là intent. Mục của một bản ghi mang thêm date của nó khi tài liệu khai một date hợp lệ trong frontmatter; bộ sinh chỉ lấy ngày từ frontmatter, không bao giờ từ văn xuôi hay quy ước tên tệp. Một index lỗi thời (một index không còn khớp với những gì các tệp index giải ra) MUST được coi là một lỗi kiểm tra. Một lớp chủ có khai báo federation.mounts còn mang, trong cùng index đó, một mảng mounts ở mức trên cùng: mỗi mount một bản ghi định tuyến (name, source, pin, trackingRef khi có khai, owner, role khi có khai, cùng siêu dữ liệu định tuyến categories / topics / requiredWhen). Đó chỉ là các bản ghi định tuyến; công cụ MUST NOT chép các mục hay văn xuôi của một lớp ngang hàng vào index của lớp chủ, và một bản ghi mount không mang theo bất cứ thứ gì mà nhóm đối tượng của lớp chủ không được phép thấy (theo distribution.md, phần Mount bị giới hạn).
  3. Changelog. Một lớp ngữ cảnh tuyên bố mức tuân thủ indexed trở lên MUST mang một changelog máy đọc được ghi lại các thay đổi của lớp ngữ cảnh. Mỗi mục mang một id ổn định, một date theo UTC, một type, một summary gói trong một dòng, và các paths bị ảnh hưởng. Thứ tự chính thống là được suy ra, không theo vị trí: công cụ MUST sắp các mục theo (date, id) tăng dần, và vị trí trong mảng không mang ý nghĩa gì. Vì id là duy nhất trong changelog (xem phần Định danh), (date, id) là một thứ tự toàn phần ngay cả khi hai thay đổi trùng date. Các mục còn sống là bất biến: công cụ MUST coi việc sửa một mục đã công bố là một lỗi kiểm tra ở bất cứ đâu nó xác lập được trạng thái trước đó, còn việc sắp xếp lại mảng thì không phải là sửa. Việc xác lập trạng thái đó cần một mốc riêng để đối chiếu; ở nơi công cụ tham chiếu chỉ có phiên bản hiện tại, như trong một bản checkout tích hợp liên tục thông thường, thì việc sửa là không nhìn thấy được đối với nó và việc xem xét bộ thay đổi mới là thứ bắt được (xem conformance.md). Changelog là một bề mặt về độ mới, không phải một kho lưu trữ: một lớp ngữ cảnh sống lâu SHOULD nén nó lại thay vì để nó phình vô hạn, và MAY nén nó bất cứ lúc nào bằng cách bỏ các mục ở đầu cũ nhất của thứ tự ấy, miễn là cùng bộ thay đổi đó ghi thêm một mục kiểu compaction với trường compacted ghi lại số lượng cùng id đầu và id cuối đã bị bỏ. Việc bỏ bất cứ thứ gì ngoài các mục cũ nhất, việc bỏ mà không kèm mục nén, và việc nén tới mức tệp rỗng, đều là lỗi kiểm tra. Kỷ luật chỉ thêm mới được khoá theo tập id và được kiểm so với trạng thái đã commit trước đó, nên nó cần git ở thời điểm soạn thảo; bản thân tệp vẫn không cần git đối với bên tiêu thụ, và lịch sử git giữ toàn bộ phần ghi chép. Một bộ thay đổi có động tới tài liệu được quản trị (những tài liệu mà các tệp index của danh mục giải ra) MUST ghi thêm một mục có paths phủ hết các đường dẫn được quản trị mà nó đã đổi, để mọi tài liệu được quản trị đã đổi đều nằm dưới một mục nào đó: kỷ luật chỉ thêm mới giữ cho các mục đã công bố là bất biến, còn quy tắc phủ này giữ cho phần ghi chép là đầy đủ. Một changelog người đọc được MAY tồn tại song song; bản ghi JSON mới là thứ công cụ đọc.
  4. Các artifact dạng frontmatter. Agent profile và bản ghi quyết định là những tài liệu markdown có frontmatter YAML hợp lệ theo schema của chúng. Phần thân văn xuôi vẫn tự do; frontmatter mới là hợp đồng với máy. Profile hay quyết định dạng JSON thuần MUST NOT bị bắt buộc: người ta còn phải đọc những tài liệu này.
  5. Định danh. Mọi giá trị id MUST ổn định một khi đã công bố: đổi tên và di chuyển thì cập nhật path, không bao giờ cập nhật id. Định danh viết thường, ngăn cách bằng dấu gạch nối, và duy nhất trong phạm vi loại artifact của nó. id của một mục index được sinh ra là suy ra được theo thứ tự ưu tiên: id trong frontmatter của tài liệu nếu nó có khai; nếu không thì id mà index đã lưu vốn đang mang cho chính đường dẫn đó, hoặc, với một lần di chuyển thuần tuý giữ nguyên nội dung, cho chính nội dung đó; nếu vẫn không thì là một slug từ tên tệp, được khử trùng lặp trong phạm vi thư mục cha. Cái đầu tiên tồn tại sẽ thắng, nên một id đã công bố sống sót qua một lần đổi tên hay di chuyển, và chỉ một tài liệu hoàn toàn mới mới đúc ra một id mới. Một tài liệu có thể vừa bị di chuyển vừa bị sửa trong cùng một bộ thay đổi thì SHOULD khai một id trong frontmatter: chỉ có frontmatter mới ghim được id qua một lần đổi đồng thời cả đường dẫn lẫn nội dung (cả hai phương án dự phòng, mang theo theo đường dẫn và mang theo theo mã băm, đều trượt), và công cụ sẽ cảnh báo (id-vanished) khi một id đã lưu biến mất, để những tham chiếu treo mà nó để lại bị bắt được.
  6. Dấu thời gian. Các giá trị date trong changelog theo ISO 8601 ở múi UTC: hoặc là một ngày theo lịch YYYY-MM-DD (được sắp như thời điểm bắt đầu ngày đó, T00:00:00Z), hoặc là một dấu thời gian tròn giây kết thúc bằng Z (ví dụ 2026-06-13T15:04:05Z). Giờ không kèm múi, độ lệch khác UTC và phần giây lẻ đều không được phép: phần giây lẻ sẽ phá vỡ bảo đảm rằng sắp date theo thứ tự từ điển cũng là sắp theo thứ tự thời gian, vì …05.1Z đứng trước …05Z trong khi thực ra lại muộn hơn. Mọi trường ngày trong mọi artifact đều bị ràng theo lịch, nên tháng 13 hay ngày 99 là không hợp lệ. Ngày ở các artifact khác cũng theo ISO 8601 và MAY chỉ gồm phần ngày. Đường dẫn theo kiểu POSIX, tương đối từ gốc kho mã nguồn, không có ./ ở đầu.
  7. Mọi artifact JSON trừ manifest MUST khai báo nhánh schema mà nó được viết theo (schemaVersion), theo versioning.md; manifest khai báo nhánh đặc tả mà nó nhắm tới bằng khoá tự đặt tên leji.
  8. Bề mặt dẫn xuất thừa hưởng các ràng buộc truy cập. Index, changelog, viewer được sinh ra, và mọi khung nhìn được biên dịch hay xuất ra từ nội dung của lớp ngữ cảnh, đều là bề mặt dẫn xuất, và đầu ra mà một agent tạo ra từ nội dung đó cũng vậy. Một bề mặt dẫn xuất mang theo các ràng buộc truy cập của phần nội dung bị giới hạn chặt nhất mà nó lấy từ đó. Một bề mặt dẫn xuất MUST NOT được ghi hay chép sang một nơi có nhóm đối tượng rộng hơn nhóm của phần nội dung ấy, nếu không có một bước biên tập che thông tin tường minh và đã được xem xét, tạo ra một bề mặt riêng cho nhóm đối tượng đó; và một agent MUST NOT trích dẫn hay tóm tắt ngữ cảnh bị giới hạn vào một bề mặt có nhóm đối tượng rộng hơn hay ít bị giới hạn hơn (một pull request, một ticket, chat, thông điệp commit, hay một lớp ngữ cảnh công khai). Index của một lớp ngữ cảnh bị giới hạn có thể nhạy cảm ngang với văn xuôi của nó: tiêu đề, đường dẫn và tóm tắt đều mô tả nó. Đây là một ràng buộc đặt lên những người và agent vận hành công cụ, chứ không phải một phép kiểm mà công cụ thực hiện: Leji không định nghĩa một mô hình nhóm đối tượng nào mà công cụ đọc được để tính ra “nhóm đối tượng rộng hơn” (quyền truy cập thuộc về hệ quản lý phiên bản, theo governance.md, phần Ranh giới truy cập), nên SDK tham chiếu không cưỡng chế nó và nhiều nhất thì một công cụ chỉ cảnh báo (bản xuất viewer cảnh báo rằng nên host nó ở chế độ riêng tư).

Định tuyến tác vụ#

Index, phép gán danh mục và bản ghi quyết định giúp agent nạp đúng lát cắt ngữ cảnh cần cho tác vụ, thay vì cả cây. Mục này định nghĩa theo nghĩa chuẩn tắc cách phạm vi tác vụ chọn ra lát cắt đó. Đây là thuật toán định tuyến duy nhất được phần còn lại của bản đặc tả dẫn chiếu: mục Nạp của boot profile (boot-profile.md) hướng agent tới đây bằng ngôn ngữ tác vụ, phạm vi của bản ghi quyết định (decisions.md) được đối sánh bằng thuật toán này, và quy trình đọc trong federation (distribution.md) dùng lại nó để xác định tác vụ chạm tới lớp ngang hàng nào. Định tuyến là việc đọc ngữ cảnh; nó không phải bao tác vụ hay giao thức thực thi, vốn vẫn nằm ngoài 1.0 (xem README.md, phần Ranh giới mở rộng).

  1. Đầu vào. Phạm vi của một tác vụ là tập các đường dẫn POSIX tương đối từ gốc kho mã nguồn mà tác vụ đọc hoặc thay đổi (đã chuẩn hoá theo Yêu cầu 6: kiểu POSIX, tương đối từ gốc, không có ./ ở đầu), cùng với mọi danh mục mà tác vụ nêu tên tường minh và mọi chủ đề mà tác vụ nêu tên tường minh. Chủ đề là đầu vào tường minh: thuật toán không bao giờ suy chúng ra từ đường dẫn, danh mục, văn xuôi hay nội dung. Việc một agent hay công cụ suy ra phạm vi từ tác vụ như thế nào thì nằm ngoài phạm vi chuẩn tắc; phần đối sánh bên dưới thì không.
  2. Đối sánh đường dẫn (theo từ vựng, hai chiều). Một đường dẫn đã khai và một đường dẫn của tác vụ khớp nhau khi, sau khi chuẩn hoá (kiểu POSIX, tương đối từ gốc, không ./ ở đầu, bỏ mọi dấu / ở cuối), hai chuỗi bằng nhau, hoặc chuỗi này là tổ tiên theo tiền tố đường dẫn của chuỗi kia: chuỗi ngắn hơn bằng đúng chuỗi dài hơn khi cắt tại một ranh giới /. Việc đối sánh là thuần tuý từ vựng: nó không bao giờ tra hệ tệp, và nó không phân biệt một đường dẫn trỏ tới tệp với một đường dẫn trỏ tới thư mục, vì sau khi chuẩn hoá thì hai thứ đó không phân biệt được. Đây là quan hệ bao hàm theo cả hai chiều (chính là quan hệ underPath mà các bản hiện thực tham chiếu dùng chung), nên một tác vụ có phạm vi rộng và một bộ chọn khai báo hẹp vẫn tìm thấy nhau, bất kể bên nào rộng hơn.
  3. Đối sánh danh mục (theo nghĩa hẹp), và hai tập danh mục. Các danh mục của một tác vụ chia thành được nở rađược phát tín hiệu. Một danh mục mà tác vụ nêu tên tường minh thì vào cả hai tập. Một đường dẫn của tác vụ mà tự nó đã là một tài liệu được quản trị (khớp với mục index sinh ra của nó theo đẳng thức chính xác, không bao giờ theo quan hệ bao hàm) sẽ đóng góp danh mục của mục đó vào chỉ tập được phát tín hiệu. Các danh mục được nở ra sẽ nạp tài liệu chủ ý và các ứng viên bản ghi của chúng; các danh mục được phát tín hiệu chỉ là tín hiệu đối sánh cho quyết định và cho mount federation, và tự chúng không nạp gì cả. Một bộ chọn danh mục MUST NOT suy ra danh mục cho một tệp bất kỳ trong kho mã nguồn, và một đường dẫn của tác vụ mà tự nó không phải một tài liệu được quản trị, kể cả mọi thư mục tổ tiên của một tài liệu như vậy, thì không đóng góp danh mục nào cả. Phạm vi theo đường dẫn với tới các tệp; việc nở danh mục thì không đi theo nó.
  4. Đối sánh chủ đề (chính xác, chỉ dành cho mount). Một chủ đề là một chuỗi không rỗng gồm các Unicode scalar value, được so sánh theo mã hoá UTF-8 của nó; một surrogate đứng lẻ không phải một chủ đề hợp lệ. Cả hai phía đều bị ràng theo quy tắc đó: một chủ đề của tác vụ hoặc một mục trong topics của một mount mà không phải chuỗi không rỗng gồm các Unicode scalar value là một lỗi đầu vào, và một bản hiện thực MUST từ chối nó chứ không trả về như một lần không khớp âm thầm. Một chủ đề của tác vụ khớp với một chủ đề đã khai khi hai chuỗi đã giải mã bằng nhau chính xác. Các bản hiện thực MUST NOT đổi hoa thường, chuẩn hoá Unicode, so sánh theo locale, cắt khoảng trắng, tách token, đối sánh chuỗi con hay đối sánh mờ ở bất kỳ phía nào, nên những cách viết tương đương chính tắc nhưng khác byte thì không khớp; quy tắc đẳng thức này tách bạch với việc sắp thứ tự theo byte của kết quả bên dưới. Các chủ đề trùng nhau của tác vụ hợp thành một tín hiệu duy nhất, nên nêu một chủ đề hai lần cũng khớp y hệt như nêu một lần. Một mount federation khớp khi bất kỳ chủ đề nào của tác vụ bằng bất kỳ chủ đề nào mà mount đó khai. Một lần khớp chủ đề chỉ chọn ra mount mà thôi: nó MUST NOT đi vào tập danh mục được nở ra hay được phát tín hiệu, không nạp tài liệu hay bản ghi nào, không định tuyến quyết định nào, không đánh giá requiredWhen, và không làm cho một mount trở thành bắt buộc.
  5. Bộ lọc trạng thái. Chỉ bản ghi quyết định nào có status còn ràng buộc mới được định tuyến như hướng dẫn hiện hành. accepteddeprecated thì ràng buộc; một bản ghi deprecated ràng buộc với một tư thế đã cũ, và một agent MUST coi nó là hướng dẫn đang trên đường rút lui chứ không phải thực hành hiện hành đã yên vị. Một bản ghi superseded MUST NOT ràng buộc, trừ với tư cách lịch sử, và MUST mang supersededBy; các bản ghi proposedrejected MUST NOT ràng buộc. Một bản ghi còn ràng buộc thì gọi là còn hiệu lực.
  6. Quyết định không phạm vi. Một bản ghi quyết định còn hiệu lực mà không khai cả affectedPaths lẫn affectedCategories là quyết định ở mức toàn tổ chức: nó được định tuyến cho mọi tác vụ, bất kể phạm vi của tác vụ là gì. Các quyết định còn hiệu lực có phạm vi thì chỉ được định tuyến khi tác vụ khớp với chúng theo đường dẫn (mục 2) hoặc theo danh mục (mục 3).
  7. Phạm vi đường dẫn rỗng, và phạm vi rỗng. Khi tập đường dẫn của tác vụ là rỗng, việc đối sánh đường dẫn không đóng góp gì và một agent MUST nói rõ rằng phần định tuyến theo đường dẫn đã không được đánh giá; các danh mục được nêu tên tường minh vẫn được tôn trọng và vẫn nở ra, còn các chủ đề được nêu tên tường minh vẫn được đối sánh. Danh mục được nêu tên và chủ đề được nêu tên đều tính là làm cho phạm vi trở nên không rỗng. Toàn bộ phạm vi của tác vụ chỉ rỗng khi nó không nêu đường dẫn nào, không nêu danh mục nào, và không nêu chủ đề nào; khi đó agent chỉ định tuyến phần ngữ cảnh vô điều kiện của boot profile và agent profile, cộng với các quyết định còn hiệu lực không phạm vi ở mức toàn tổ chức. Một agent MUST NOT trình bày một lần nạp không định tuyến như thể nó có phạm vi.
  8. Bản ghi được định tuyến như ứng viên. Một danh mục được nở ra sẽ định tuyến các tài liệu chủ ý của nó như ngữ cảnh bắt buộc; còn các bản ghi của danh mục đó được trả về riêng, mỗi bản kèm loại và ngày của nó, như những ứng viên mà người đọc nạp theo phán đoán của mình. Một bản ghi chỉ trở thành bắt buộc khi đường dẫn của tác vụ chọn thẳng nó theo mục 2, khi một agent hay boot profile nêu tên nó, hoặc khi một người yêu cầu nó; việc khớp danh mục, hay việc mang ngày mới nhất, không bao giờ làm cho một bản ghi trở thành bắt buộc. Ở nơi một bản ghi vừa là ứng viên theo danh mục vừa được chọn trực tiếp theo đường dẫn, thì lựa chọn trực tiếp thắng và nó là bắt buộc. Việc định tuyến MUST NOT chứng nhận bản ghi nào là “mới nhất” hay “bản hiện hành”: 1.0 không định nghĩa danh tính hay bảo đảm về thứ tự cho một chuỗi bản ghi, nên phán đoán về độ mới thuộc về người đọc, dựa trên những ngày mà index làm nổi lên. Bản ghi quyết định giữ cách định tuyến riêng của chúng (mục 5 và 6) và không bao giờ được định tuyến như bản ghi thông thường. Việc nêu tên một tệp quyết định như một đường dẫn của tác vụ không định tuyến quyết định đó; phạm vi mà nó khai báo mới làm việc ấy.
  9. Trích dẫn. Một agent có nạp các bản ghi quyết định đã định tuyến MUST trích dẫn những bản ghi đã khớp mà nó nạp, để người đọc thấy được agent đã áp dụng hướng dẫn nào và suy ra được nó đã không áp dụng những gì.

Lát cắt đã định tuyến là những gì một agent nạp cho một tác vụ. Nó là hợp của: tập nạp vô điều kiện của boot profile và requiredRead của agent profile đang hoạt động, thứ mà agent giữ như phần nền độc lập với mọi phạm vi; mọi tài liệu chủ ý được quản trị trong một danh mục được nở ra; mọi mục được quản trị mà đường dẫn của tác vụ chọn theo mục 2; mọi bản ghi được chọn trực tiếp theo đường dẫn theo mục 8; và mọi quyết định còn hiệu lực mà tác vụ khớp theo đường dẫn hoặc theo một danh mục được phát tín hiệu hay được nở ra, cộng với các quyết định còn hiệu lực không phạm vi ở mức toàn tổ chức.

Các danh mục được phát tín hiệu chỉ đóng góp việc đối sánh, cho quyết định và cho mount federation, và không bao giờ nở ra một kho ngữ liệu nào.

Lát cắt không đồng nghĩa với toàn bộ gói kết quả. Công cụ tính kết quả định tuyến sẽ trả về lát cắt cùng phần tài liệu mà agent không được nạp khi chưa có yêu cầu: các ứng viên bản ghi và siêu dữ liệu định tuyến đi kèm. Nạp toàn bộ gói kết quả sẽ làm mất đi mục đích của việc định tuyến.

Thứ tự kết quả là chuẩn tắc ở nơi một công cụ có phát ra kết quả, để các bản hiện thực độc lập nhất trí với nhau tới từng byte: danh mục theo đúng thứ tự danh mục chính tắc của bản đặc tả này; tài liệu, bản ghi và quyết định theo đường dẫn tăng dần; mount theo tên tăng dần. Việc so sánh chuỗi là theo byte trên UTF-8, không phụ thuộc vào locale hay vào phép đối chiếu theo codepoint.

Công cụ MAY cung cấp một hàm trợ giúp tính ra lát cắt này từ một tập đường dẫn; các bản hiện thực tham chiếu có phơi ra một hàm như vậy (route). Một hàm như thế tính phần phụ thuộc phạm vi và không bắt buộc phải phát ra phần nền, vốn là thứ bên gọi đã cầm sẵn; nghĩa vụ nạp phần nền đó của agent thì không đổi. Việc định tuyến là tuân thủ bất cứ khi nào một người đọc thô đi theo đúng thuật toán này.

Ghi chú (tham khảo)#

Công cụ tham chiếu hôm nay kiểm schema của changelog và kỷ luật chỉ thêm mới (leji validate chạy cả hai); còn việc xác minh độ phủ của changelog so với một phiên bản gốc, tức mọi đường dẫn được quản trị đã đổi đều xuất hiện trong một mục được ghi thêm, là một phép kiểm dạng báo cáo còn nằm trong lộ trình, chưa phải một cổng chặn. Cho tới khi có nó, độ phủ dựa vào kỷ luật xem xét và CI được khai theo quy trình (xem conformance.md).

Index là nguồn điều hướng cho phần ngữ cảnh được quản trị: leji viewer dùng index để dựng bộ khung của phần này, rồi đặt cây thư mục của kho mã nguồn bên dưới như một vùng tham khảo có thể xem xét. Nhờ đó, cùng một khung nhìn cho thấy cả ngữ cảnh được quản trị lẫn hệ thống điều hướng sẵn có của nhóm. Bất kỳ công cụ tài liệu nào cũng có thể trình bày index theo cách tương tự. Cách trình bày không mang tính chuẩn tắc. Bề mặt này được chủ ý giữ nhỏ. Năm hình thái đủ để công cụ kiểm tra lớp ngữ cảnh, so sánh thay đổi, đánh giá độ tươi và định tuyến agent tới đúng lát cắt, đồng thời vẫn đủ gọn để cả nhóm ghi nhớ. Mọi thứ vượt ra ngoài năm hình thái này đều thuộc giai đoạn sau 1.0 và phải chờ thực tiễn chứng minh.

Quyết định ↗pull request

Quyết định

Bản ghi quyết định là phần ghi chép có ngày tháng của lớp ngữ cảnh về lý do: các quyết định kiến trúc, lựa chọn nhà cung cấp, ranh giới phạm vi và cả quyết định chủ ý không thực hiện một việc nào đó. Chúng giúp tránh tranh luận lại vấn đề cũ và cung cấp cho agent cả lập luận, thay vì chỉ một quy tắc đơn lẻ.

Bản ghi quyết định là dạng record chính thức (xem content-categories.md, phần Chủ ý và bản ghi): vốn dĩ đã là record, với schema và vòng đời thống nhất mà record thông thường không có. Mục index được sinh từ chúng mang kind: record; bản thân bản ghi quyết định không khai báo khoá kind (schema là đóng, nên nếu khai báo tường minh kind, bước kiểm tra sẽ thất bại).

Yêu cầu#

  1. Bản ghi quyết định là markdown kèm frontmatter YAML hợp lệ theo decision-record.schema.json, mỗi tệp một bản ghi. Kho quyết định là hợp của hai bề mặt được manifest khai báo, và một lớp ngữ cảnh MAY dùng một trong hai hoặc cả hai: đường dẫn bản ghi được khai báo (machine.decisionRecordsPath, mặc định <root>/decisions/) và những mục mà tệp index của danh mục decisions trỏ tới. Một bản ghi MUST tới được qua ít nhất một trong hai đường đó.
  2. Frontmatter MUST mang: id (ổn định), title, status, và date. status là một trong proposed, accepted, superseded, deprecated, rejected.
  3. Phần thân MUST nêu rõ, bằng văn xuôi: bối cảnh (tình huống nào buộc phải ra quyết định), bản thân quyết định, và các hệ quả của nó. Các tiêu đề mục RECOMMENDED## Context, ## Decision, ## Consequences; một bản ghi MAY thêm ## Alternatives.
  4. Bản ghi là lịch sử chỉ thêm mới: một bản ghi MUST NOT bị sửa thành một quyết định khác. Hai trường frontmatter là có thể thay đổi khi quyết định già đi, là status (vòng đời của nó) và supersededBy (đặt khi nó bị thay thế); mọi thứ còn lại, gồm id, titledate gốc, phạm vi đã khai báo, và phần thân văn xuôi, là bất biến một khi đã công bố. Một sự đảo ngược hay thay đổi là một bản ghi mới, với frontmatter đặt supersedes, còn status của bản ghi cũ trở thành superseded kèm supersededBy. Liên kết thay thế MUST nhất quán theo cả hai chiều: khi bản ghi B đặt supersedes: A, bản ghi A mang status: superseded và có supersededBy: B, và một bản ghi superseded MUST nêu tên bản kế nhiệm của nó trong supersededBy. Cả hai bản ghi đều ở lại. Công cụ tham chiếu hôm nay đã cưỡng chế tính nhất quán hai chiều của liên kết thay thế. Nó chưa tự kiểm tra tính bất biến (rằng các trường đã đóng băng và phần thân của một bản ghi đã công bố không đổi so với một phiên bản gốc); đó là một phép kiểm tra dạng báo cáo còn nằm trong lộ trình, chưa phải một cổng chặn. Cho tới khi có nó, tính bất biến dựa vào kỷ luật xem xét được khai theo quy trình (xem conformance.md).
  5. Một bản ghi MAY khai báo affectedPathsaffectedCategories, để công cụ định tuyến từ phạm vi của một tác vụ tới những quyết định chi phối nó. Cách phạm vi của một tác vụ chọn ra bản ghi (đối sánh đường dẫn có tính tới chồng lấn, khớp danh mục theo nghĩa hẹp, ràng buộc accepted / deprecated, và cách xử lý ở mức toàn tổ chức với một bản ghi không khai báo trường nào trong hai trường đó) là thuật toán Định tuyến tác vụ trong machine-readable-surface.md.
  6. Đề xuất bị bác cũng là bản ghi (status: rejected). Một quyết định không làm, được viết ra, là cách rẻ nhất để khỏi phải bàn lại.

Tương thích với ADR (tham khảo)#

Bản ghi quyết định của Leji được thiết kế để tương thích với Architecture Decision Record: một thư mục ADR hiện có có thể đáp ứng decisions bằng cách thêm các trường frontmatter vào từng bản ghi (hoặc chỉ các bản ghi mới từ nay về sau), rồi ánh xạ thư mục trong manifest. Không bắt buộc phải dùng công cụ ADR nào, nhưng cũng không loại trừ công cụ nào.

Quản trị ↗pull request

Quản trị

Quản trị là yếu tố phân biệt lớp ngữ cảnh với wiki. Mô hình của nó là một vòng tròn: bình đẳng về quyền đọc, không bình đẳng về quyền quyết.

Vòng tròn, theo nghĩa chuẩn tắc#

  1. Ai cũng đọc. Mọi bên tham gia, cả người lẫn agent, có quyền truy cập một lớp ngữ cảnh MUST đọc được toàn bộ lớp đó. Một lớp ngữ cảnh có chặn quyền đọc theo vai trò ở bên trong nó thì không phải một lớp ngữ cảnh dùng chung; ở nơi những người khác nhau được đọc những phần khác nhau, thì phần nội dung đó thuộc về những lớp ngữ cảnh riêng (xem Ranh giới truy cập bên dưới và distribution.md).
  2. Ai cũng đề xuất. Mọi bên tham gia, người hay agent, MAY đề xuất thay đổi cho lớp ngữ cảnh. Đề xuất do agent soạn là hạng nhất: một agent phát hiện ngữ cảnh thiếu hoặc sai trong lúc làm việc SHOULD đề xuất phần sửa ngay trong cùng bộ thay đổi đã làm lộ ra vấn đề. Một đề xuất SHOULD mang theo phần lý giải đủ để người xem xét hiểu chủ ý và tác động dự kiến của nó; phần lý giải đó là mức tối thiểu mà một người cần để phê duyệt. Leji 1.0 không định nghĩa một giao thức bằng chứng tổng quát nào (xem Phạm vi của 1.0).
  3. Người phê duyệt. Mọi thay đổi đối với lớp ngữ cảnh MUST được một người phê duyệt trước khi trở thành chính thống. Việc phê duyệt đi trên chính cơ chế xem xét sẵn có của kho mã nguồn (pull request); Leji không đưa vào một quy trình riêng nào. Việc tham gia MAY diễn ra qua bất kỳ giao diện nào, nhưng việc phê duyệt chính thống MUST là một bản ghi xem xét kiểm toán được trong chính cơ chế đó: quy được trách nhiệm cho người phê duyệt và gắn với đúng bộ thay đổi đang được xem xét. Một sự phê duyệt chỉ được nói ra trong thảo luận bên ngoài, trong chat, trong trạng thái một ticket, hay trong bình luận tài liệu, thì không được tính cho tới khi nó trở thành một bản ghi như vậy; và việc chép nó lại vào một bình luận cũng không tính. Mở rộng cách người ta tham gia không bao giờ dịch chuyển nơi thẩm quyền được ghi lại.

Yêu cầu#

  1. Quyền sở hữu, không phải quyền tác giả. Manifest MUST nêu tên một chủ sở hữu chính (owners.primary) và MAY nêu tên một chủ sở hữu kế tục (owners.continuity): một người khác, gánh cùng phần trách nhiệm đó khi người chính vắng mặt hoặc rời đi. Một lần áp dụng có hỗ trợ từ bên ngoài SHOULD nêu tên chủ sở hữu kế tục trước khi bên hỗ trợ rút đi; một lớp ngữ cảnh của người làm một mình MAY không có ai, và điều đó nói thẳng ra rằng nó không có phương án kế tục. Chủ sở hữu là những con người chịu trách nhiệm: agent đề xuất và xem xét nhưng không bao giờ sở hữu, và việc nêu lại tên người chính vào ô kế tục thì chẳng cung cấp phương án nào. Chủ sở hữu chịu trách nhiệm về sức khoẻ của lớp ngữ cảnh: rằng nó luôn cập nhật, rằng nội dung lỗi thời hay mâu thuẫn được tỉa đi, và rằng mọi mảng đều có người chăm. Chủ sở hữu không phải người biên tập. Nội dung được cả vòng tròn viết ra và giữ cho đúng ngay trong lúc làm việc; dồn việc đó vào một người giữ đền chính là nút thắt cổ chai mà mô hình này sinh ra để tránh.
  2. Phạm vi xem xét. Thay đổi trong lớp ngữ cảnh SHOULD được xem xét bởi những người gần nội dung bị ảnh hưởng nhất, tức chủ mảng, chứ không dồn hết qua một người gác cổng duy nhất. Quyền sở hữu theo mảng chính là bản đồ sở hữu sẵn có của kho mã nguồn (một tệp CODEOWNERS, một quy ước của nhóm), không phải một trường mới trong manifest; Leji dùng lại nó đúng như cách nó dùng lại pull request cho việc phê duyệt. Chủ sở hữu chính chịu trách nhiệm bảo đảm mảng nào cũng có người. Việc xem xét hỏi nhiều hơn câu “cái này có đúng không?”: vì sao nội dung này thuộc về lớp ngữ cảnh, ai sẽ dựa vào nó, điều gì cho thấy nó đứng vững, và khi nào thì nên xem lại. Một thay đổi không trả lời được những câu đó thì là một đường link hay một ghi chú, chứ không phải ngữ cảnh chính thống. Câu hỏi xem xét thường trực cho mọi bộ thay đổi là thay đổi này có làm đổi ngữ cảnh không?; nếu có, thì phần chênh lệch ngữ cảnh thuộc về cùng bộ thay đổi ấy.
  3. Đưa vào và loại bỏ. Đề xuất thì mở; đưa vào thì không. Nội dung chỉ thuộc về lớp ngữ cảnh nếu nó làm đổi cách công việc sau này được làm: nó đặt ra một ràng buộc, mã hoá một quyết định, định nghĩa một giao diện hay một ranh giới sở hữu, hoặc chặn một sai lầm lặp lại. Mọi thứ khác thì được liên kết tới, không hấp thụ vào. Lớp ngữ cảnh MUST có một con đường loại bỏ chủ đích ngang với con đường phê duyệt của nó: nội dung lỗi thời, bị thay thế và trùng lặp được tỉa đi ngay trong các bộ thay đổi được xem xét thông thường, và việc tỉa là một phần bổn phận của từng chủ mảng, không phải một dự án dọn dẹp riêng. Một lớp ngữ cảnh chỉ có phình ra là một lớp đang mục ruỗng trong khi vẫn qua được vòng xem xét. Hướng dẫn có tính bền vững SHOULD vượt qua cổng chứng minh qua hai lần (theo content-categories.md): một bản vá dùng một lần thì cứ merge, nhưng một chuẩn mực chỉ trở thành chính thống sau khi nó đứng vững qua ít nhất hai tác vụ thật. Hãy đúc kết cái đang đúng, chứ không phải cái đang mong.
  4. Kỷ luật changelog. Từ mức tuân thủ indexed trở lên, mọi thay đổi đã phê duyệt của lớp ngữ cảnh MUST ghi thêm một mục changelog máy đọc được theo machine-readable-surface.md.
  5. Độ tươi. Độ tươi là một cơ chế của chủ ý: các tài liệu chủ ý và agent profile SHOULD mang hạn xem xét lại (freshness.reviewAfter trong mục index và trong profile), còn một bản ghi thì không mang gì cả (ngày của nó chính là tính cập nhật của nó, theo content-categories.md; một hạn xem xét lại khai trên một bản ghi là một lỗi kiểm tra). Công cụ SHOULD báo cáo những nội dung chủ ý đã quá hạn, và MUST NOT âm thầm coi nội dung lỗi thời là còn hiệu lực. Một người đọc nạp ngữ cảnh cho một tác vụ MUST làm nổi lên, ngay trong đầu ra của tác vụ đó, mọi mục đã nạp mà hạn xem xét lại đã trôi qua, để sự lỗi thời hiện ra trước mắt người chứ không bị chôn đi. Việc bản ghi kế tiếp được chờ đợi trong một chuỗi vận hành có quá hạn hay không là một khái niệm khác (độ mới của dòng bản ghi); 1.0 gọi tên nó nhưng không định nghĩa cơ chế nào cho nó. Ngữ cảnh bắt buộc của một tác vụ là hợp của tập nạp vô điều kiện trong boot profile, requiredRead của agent profile đang hoạt động, và lát cắt mà thuật toán Định tuyến tác vụ chọn cho tác vụ đó (các quyết định còn hiệu lực đã định tuyến và các tài liệu chủ ý được quản trị đã định tuyến, cộng với mọi bản ghi mà đường dẫn của tác vụ chọn trực tiếp, theo machine-readable-surface.md). Khi hạn của một mục bắt buộc đã hết, người đọc MUST dừng lại hoặc hỏi chứ không đi tiếp trên mục đó; hành động dựa trên ngữ cảnh đã biết là quá hạn xem xét chính là kiểu hỏng do âm thầm dùng nội dung lỗi thời mà quy tắc này sinh ra để ngăn. Một mục lỗi thời nhưng không bắt buộc thì MAY được dùng, kèm ghi chú rằng nó đã lỗi thời. Ở mức tuân thủ governed, hạn tươi MUST được khai báo và được kiểm (theo danh sách kiểm mức tuân thủ, kiểm ở dạng chỉ báo cáo là chấp nhận được); chạy phép kiểm đó trong CI là RECOMMENDED. Độ tươi về mặt xem xét lại (nói trên) khác với tính cập nhật của bản checkout: tức bản sao mà người đọc đang cầm có khớp với kho mã nguồn chính thống hay không. Người đọc xác lập tính cập nhật của bản checkout từ hệ quản lý phiên bản (git); cây làm việc chỉ cập nhật tới đúng phiên bản đã checkout, và công cụ MUST NOT âm thầm coi một bản sao chưa xác minh là còn hiệu lực. Một người đọc tiếp cận lớp ngữ cảnh dưới dạng nội dung tệp thuần, không có cây làm việc git hay siêu dữ liệu phiên bản nào truy cập được (nội dung tệp được tải lên hay đồng bộ sang một giao diện khác, không kèm kho mã nguồn), MUST coi tính cập nhật của bản checkout là không rõ chứ không coi là còn hiệu lực.
  6. Nội dung chính thống sống trong lớp ngữ cảnh. Tri thức chi phối cách công việc được làm MUST NOT chỉ tồn tại trong một tệp cấu hình của nhà cung cấp, một luồng chat, hay sổ tay riêng của một cá nhân. Nếu nó chi phối công việc, nó thuộc về lớp ngữ cảnh, dưới vòng xem xét.

Ranh giới truy cập#

Leji không định nghĩa cơ chế kiểm soát truy cập nào của riêng mình. Quyền truy cập một lớp ngữ cảnh do hệ quản lý phiên bản (git) và nền tảng nơi kho mã nguồn nằm chi phối: phân quyền của bên chủ quản kho mã nguồn, cùng hệ tệp hay ổ đĩa dùng chung để lộ cây làm việc. Lớp ngữ cảnh chính là đơn vị của quyền truy cập.

  1. Một lớp ngữ cảnh MAY nằm trong một kho mã nguồn có kiểm soát truy cập. Leji không cấp, không kiểm và không cưỡng chế quyền truy cập đó; hệ quản lý phiên bản và bên chủ quản của nó mới làm việc ấy.
  2. Một lớp ngữ cảnh tuân thủ MUST NOT đòi hỏi việc chặn quyền đọc theo vai trò ở bên trong chính nó. Câu “ai cũng đọc được” chỉ áp trong phạm vi nhóm đối tượng của một lớp ngữ cảnh: mọi người mà hệ quản lý phiên bản cho vào đều đọc được toàn bộ lớp ngữ cảnh đó.
  3. Nội dung cần một nhóm đối tượng hẹp hơn (ngữ cảnh cho ban điều hành, cho tài chính, cho an ninh, hay cho ứng cứu sự cố) MUST nằm trong một lớp ngữ cảnh riêng, với kho mã nguồn, manifest, chủ sở hữu và cổng xem xét của riêng nó, được phân quyền bởi hệ quản lý phiên bản. Ngữ cảnh bị giới hạn là một lớp ngữ cảnh riêng, không bao giờ là một vùng bị giới hạn bên trong một lớp dùng chung.
  4. Việc ghép một lớp bị giới hạn vào ngữ cảnh của một nhóm khác là trường hợp federation, kèm các quy tắc bổ sung cho mount bị giới hạn trong distribution.md.

Mô hình bảo trì (tham khảo)#

Lớp ngữ cảnh được bảo trì qua từng thay đổi, ngay trong dòng công việc đang diễn ra: một tác vụ làm lộ ra ngữ cảnh thiếu hoặc sai; phần sửa nằm trong cùng bộ thay đổi được xem xét; changelog ghi lại thay đổi đó. Không cần một sprint riêng để viết tài liệu. Ngữ cảnh sai tạo ra đầu ra sai mà ai đó sẽ nhận thấy ngay trong ngày; phản hồi đó, kết hợp với vòng xem xét và CI chạy các phép kiểm cơ chế, tạo thành toàn bộ hệ thống cưỡng chế.

Vòng phản hồi nhanh phát hiện nội dung sai rất sớm. Còn sự tích tụ âm thầm của nội dung tầm thường hoặc dư thừa được xử lý bằng ngưỡng đưa vào và con đường loại bỏ nói trên, do chính chủ sở hữu từng mảng áp dụng. Đây là công việc biên tập mà Leji chủ ý phân tán. Một biên tập viên duy nhất có vẻ là cách an toàn để giữ chất lượng, nhưng thực tế lại trở thành điểm chậm nhất của hệ thống: thay đổi phải xếp hàng chờ hoặc đi vòng, trong khi người đó có ít ngữ cảnh hơn chuyên gia của từng mảng. Khi ấy, lớp ngữ cảnh sẽ đình trệ hoặc rẽ nhánh. Mỗi chủ mảng tự tinh gọn và trông coi phần của mình mới giúp toàn bộ lớp ngữ cảnh vừa gọn vừa chính xác mà không tạo nút thắt. Chủ sở hữu chăm lo sức khoẻ của hệ thống; vòng tròn chăm lo nội dung.

Phân phối ↗pull request

Phân phối

Phần này quy định vị trí của lớp ngữ cảnh so với công việc mà nó mô tả. Có ba khuôn mẫu cùng một quy tắc xuyên suốt: lớp ngữ cảnh chỉ gồm tài liệu và MUST NOT đưa một phụ thuộc lúc build hay lúc chạy vào bất kỳ kho mã nguồn tiêu thụ nào.

Khuôn mẫu 1: Monorepo (mặc định)#

Lớp ngữ cảnh nằm trong cùng kho mã nguồn với phần code và hạ tầng mà nó mô tả, tại gốc ngữ cảnh. Đây là khuôn mẫu RECOMMENDED ở bất cứ đâu công việc của nhóm nằm trong một kho mã nguồn: code, hạ tầng và ngữ cảnh cùng được đánh phiên bản với nhau, và sự trôi lệch trở nên khó xảy ra về mặt cấu trúc.

Khuôn mẫu 2: submodule chỉ gồm tài liệu cho tình huống nhiều kho mã nguồn#

Khi công việc trải trên nhiều kho mã nguồn, lớp ngữ cảnh nằm trong một kho ngữ cảnh riêng, và các kho mã nguồn tiêu thụ mount nó vào như một git submodule.

  1. Kho ngữ cảnh là một kho git bình thường với leji.json, chính sách nhánh và cổng xem xét của riêng nó.
  2. Các kho mã nguồn tiêu thụ MUST mount nó ở một đường dẫn cố định (RECOMMENDED: context/) và MUST NOT gắn bất kỳ bước build hay bước chạy nào vào sự hiện diện của nó: một mount thiếu hoặc lỗi thời làm suy giảm tri thức, không bao giờ làm hỏng bản build.
  3. Mỗi kho mã nguồn tiêu thụ ghim một phiên bản cụ thể của lớp ngữ cảnh. Các lần cập nhật pin MUST đến dưới dạng những bộ thay đổi xem xét được (pull request do script hoặc bot mở), để thay đổi ngữ cảnh là nhìn thấy được, xem xét được, và quy được trách nhiệm theo từng kho mã nguồn.
  4. Công cụ SHOULD báo cáo pin lỗi thời (mỗi kho mã nguồn tiêu thụ đang tụt lại bao xa so với lớp ngữ cảnh). Việc báo cáo pin lỗi thời MUST đi trước mọi hình thức cưỡng chế có tính chặn: nhìn thấy trước, dựng cổng sau. Phần báo cáo pin của SDK tham chiếu 1.0 phủ các mount federation (khuôn mẫu 3); còn với các pin phía tiêu thụ của khuôn mẫu này thì nó không kèm phép kiểm nào, nên ở mức federated mục đó là khai theo quy trình (xem conformance.md); một nhóm hoặc công cụ của chính nhóm đó báo cáo phần này cho tới khi có một phép kiểm tham chiếu.

Khuôn mẫu 3: federation của các lớp ngữ cảnh ngang hàng#

Khuôn mẫu 1 và 2 mỗi khuôn mẫu chỉ có một lớp ngữ cảnh: một monorepo sở hữu một lớp, còn một tổ chức nhiều kho mã nguồn thì tiêu thụ một lớp. Federation là khuôn mẫu dành cho một tổ chức nơi đã có nhiều hơn một nhóm sở hữu lớp ngữ cảnh của riêng mình, và mục tiêu là làm cho các lớp ngữ cảnh đó đọc được lẫn nhau mà không ai phải nhường quyền kiểm soát.

Phản xạ đầu tiên thường là gộp tất cả vào một kho ngữ cảnh duy nhất chứa tri thức của mọi nhóm. Đừng làm vậy. Lớp ngữ cảnh luôn cập nhật vì chính chủ sở hữu đọc nó trong mọi tác vụ và sửa chỗ sai ngay trong cùng bộ thay đổi. Khi đưa ngữ cảnh của nhóm sản phẩm vào kho mã nguồn của nhóm nền tảng, bạn tách nội dung sản phẩm khỏi trách nhiệm của nhóm sản phẩm; nội dung sẽ dần xuống cấp trong khi ai cũng tưởng đã có người khác phụ trách. Tập trung hoá tri thức chỉ tái tạo nút thắt từng đẩy tri thức vào đầu mỗi người và các luồng chat ngay từ đầu.

Federation ghép các lớp ngữ cảnh lại thay vì hấp thụ chúng. Lớp ngữ cảnh của một nhóm gia nhập đồ thị của một nhóm khác với tư cách một lớp ngang hàng: được mount, được tham chiếu và được đọc, không bao giờ bị sao chép.

  1. Một lớp ngữ cảnh ngang hàng gia nhập dưới dạng một mount có ghim, khai báo trong federation.mounts của manifest lớp chủ: nameowner của lớp ngang hàng, source là định vị kho mã nguồn của nó, và một pin nêu id commit bất biến đầy đủ của phiên bản lớp ngang hàng mà lớp chủ đọc. Pin là phiên bản của mount được ghi nhận chính thức, giữ ngay trong manifest để mỗi lần cập nhật pin đều đến dưới dạng một bộ thay đổi xem xét được: một mount ghi lại phiên bản nào trong sự thật của nhóm khác mà kho mã nguồn này đang đọc, chứ không phải một nhánh rẽ của nó. Một mount MAY khai trackingRef, tức một nhánh hoặc tag có tên đầy đủ trên nguồn, dùng làm mốc để xét độ lỗi thời và khả năng với tới; nếu vắng, nhánh mặc định được nguồn quảng bá sẽ được dùng tại thời điểm kiểm và được nêu tên trong báo cáo.

  2. Lớp ngang hàng giữ lại mọi thứ khiến nó còn sống: kho mã nguồn, chủ sở hữu, cổng xem xét, changelog và tuyên bố mức tuân thủ của riêng nó. Lớp chủ MUST NOT sao chép nội dung của lớp ngang hàng vào chính mình. Nội dung bị tách khỏi nhóm sở hữu nó sẽ lỗi thời mà chẳng ai chịu trách nhiệm, và đó đúng là kiểu hỏng mà federation sinh ra để ngăn.

  3. Nội dung đã mount hiện thực hoá thành một phép chiếu lớp do resolver nạp về, không bao giờ là một bản sao đã commit. Một lớp ngang hàng thường là một lớp nhúng trong một kho mã nguồn lớn hơn (khuôn mẫu 1), nên checkout nguyên cả lớp ngang hàng sẽ thành ra nhập cả một sản phẩm về chỉ để đọc ngữ cảnh của nó. Thay vào đó, công cụ trích ra phép chiếu lớp tại pin, tức hợp đã khử trùng lặp của mọi thứ mà chính manifest của lớp ngang hàng làm cho đọc được (tệp leji.json ở gốc, cây dưới gốc ngữ cảnh đã khai báo, boot profile, các tệp index và changelog máy đọc được khi có mặt tại pin, các cây agent-profile và decision-record khi có mặt, mọi agent profile được các liên kết agents nêu tên, mọi tệp index danh mục, và mọi đường dẫn được quản trị mà context index đã sinh ra tại pin liệt kê, bất kể chúng nằm ở đâu; chính manifest của lớp ngang hàng định nghĩa phép chiếu của nó, lớp chủ không bao giờ tự biên tập), rồi đưa vào một bộ nhớ đệm tạm mà hệ quản lý phiên bản của lớp chủ bỏ qua. Ranh giới lỗi cũng chạy theo đúng đường đó: một tệp được tham chiếu hoặc bị schema đòi hỏi mà vắng mặt tại pin (boot profile, một index danh mục, một agent profile đã gắn, một đường dẫn được quản trị có trong index) sẽ làm phép chiếu thất bại với một mã ổn định nêu tên artifact đã khai và đường dẫn còn thiếu, trong khi một thư mục vắng mặt hay một artifact máy đọc được vắng mặt thì không đóng góp gì và cũng không làm hỏng gì, bất kể vị trí thực tế của nó là do khai báo hay do mặc định; git không biểu diễn được một thư mục rỗng, và một lớp không có index được sinh ra thì đơn giản là không có bao đóng nội dung nào ngoài cây gốc của nó. Một thất bại phép chiếu thuộc nhóm lỗi về tính sẵn có (nội dung tại pin thiếu hoặc sai khuôn dạng) chỉ khiến mount không sẵn có trên máy này, và không bao giờ làm hỏng phép kiểm thông thường của lớp chủ hay bản build sản phẩm của lớp chủ. Một thất bại phép chiếu thuộc lớp an toàn hoặc lớp nội bộ (một đường dẫn thoát ra ngoài, một chuỗi sai khuôn dạng, một giới hạn bị vượt) sẽ huỷ việc nạp về với mã thoát khác không, và không bao giờ có phép chiếu dở dang nào được công bố. Các byte tại pin được giải từ một kho đối tượng git (một kho gợi ý cục bộ trên máy, kho mã nguồn do resolver quản lý, hoặc cơ sở dữ liệu đối tượng của một submodule trong lớp chủ), không bao giờ từ một cây làm việc nào, và việc truy cập mạng chỉ diễn ra như một bước tường minh đã được đồng ý. Một kho mã nguồn chủ MAY mang một submodule của lớp ngang hàng vì lý do riêng của nó; công cụ chỉ coi đó như một kho đối tượng cục bộ nữa mà thôi, và một bản checkout không bao giờ là nội dung đã mount đọc được. Việc commit nội dung của lớp ngang hàng vào lớp chủ, kể cả nội dung bộ nhớ đệm, là không tuân thủ. Quy tắc chỉ gồm tài liệu từ khuôn mẫu 2 đứng vững theo cấu tạo: không có gì trong lớp chủ build hay chạy dựa trên phép chiếu.

    Một đường dẫn machine giải ra thành gốc kho mã nguồn thì không chọn gì cả. Một machine.agentProfilesPath hay machine.decisionRecordsPath đã khai mà giải ra thành gốc kho mã nguồn của lớp ngang hàng thì không đóng góp lựa chọn thư mục nào cho phép chiếu: tôn trọng nó sẽ thành ra nhập nguyên cả kho mã nguồn của lớp ngang hàng về, đúng cái kết cục mà phép chiếu lớp sinh ra để tránh. Không có gì được tham chiếu bị mất cả, vì các profile và bản ghi quyết định được nêu tên riêng lẻ, qua các liên kết agents hoặc qua index đã sinh ra tại pin, thì vẫn đi cùng; chỉ có phép chọn gốc theo kiểu vơ đũa cả nắm là bị bỏ.

    Giới hạn của phép chiếu. Một resolver MUST cưỡng chế bốn cận, để một bản hiện thực độc lập từ chối đúng những đầu vào ấy thay vì mỗi bên tự chọn trần của mình. Một phép chiếu mang tối đa 65.536 mục, đếm sau khi đã khử trùng lặp. Tổng nội dung của nó tối đa 2 GiB (2.147.483.648 byte). Không đường dẫn được chiếu nào vượt quá 4.096 byte, đo theo mã hoá UTF-8 của đường dẫn chứ không theo ký tự, code point, hay đơn vị chuỗi bản địa của một runtime nào, vì những thứ đó khác nhau giữa các bản hiện thực và sẽ dẫn tới việc chấp nhận những đường dẫn khác nhau. Một lần liệt kê nguyên cây chiếm tối đa 256 MiB (268.435.456 byte) khi truyền; con số này chặn phần siêu dữ liệu liệt kê mà một resolver đọc để chọn lựa, chứ không chặn phần nội dung được chiếu vốn đã có giới hạn byte, và hai con số cố ý khác nhau vì một kho mã nguồn lớn vẫn có thể chứa một phép chiếu hợp lệ và hoàn toàn nhỏ. Vượt quá bất kỳ cận nào trong bốn cận đó là một thất bại phép chiếu thuộc lớp an toàn.

  4. Một mount chưa được hiện thực hoá làm suy giảm tri thức, không bao giờ làm hỏng bản build. Việc kiểm tra tách bạch ba mối quan tâm. Một manifest nói dối là một lỗi: tên mount trùng nhau, một mount dùng lại chính name của lớp chủ, hay một source hoặc pin thiếu hoặc sai khuôn dạng. Một mount đã khai mà đơn giản là chưa được nạp về trên máy này thì là một cảnh báo: đó là sự suy giảm tính sẵn có một cách trung thực, được báo cáo rồi bỏ qua. Tính toàn vẹn của một phép chiếu đã hiện thực hoá so với pin của nó là một chẩn đoán do công cụ làm nổi lên, và chỉ trở thành chí mạng khi có bật cưỡng chế. Việc kiểm tra thông thường MUST NOT thất bại, tự tải về, hay hỏi han chỉ vì một mount không sẵn có; những lớp chủ muốn cưỡng chế thì bật nó lên một cách tường minh (một phép kiểm sức khoẻ federation MAY nạp về rồi đòi hỏi tính sẵn có), còn nghĩa vụ của người đọc đối với một mount bắt buộc cho tác vụ mà không sẵn có thì theo quy tắc đóng khi lỗi bên dưới.

  5. Mount cho phép đọc, không trao thẩm quyền, và không cấp quyền truy cập. Một lớp chủ có mount một lớp ngang hàng sẽ định tuyến người đọc và agent vào đó khi họ vốn đã có quyền truy cập nó; việc mount không cấp quyền truy cập ấy, cũng không phê duyệt các thay đổi của lớp ngang hàng. Việc ghi vào từng lớp ngữ cảnh vẫn do chính chủ sở hữu của nó phê duyệt, và ai được đọc nó thì vẫn là việc của hệ quản lý phiên bản. Federation ghép ra phần ngữ cảnh đọc được cho những bên tham gia mà các kho mã nguồn liên quan vốn đã cho vào; nó để nguyên chuyện ai phê duyệt và ai được đọc ở đúng chỗ cũ.

  6. Mount là trực tiếp và phẳng. Một lớp chủ ghép chính những lớp ngang hàng mà nó nêu tên; công cụ MUST NOT đệ quy vào các mount của một lớp ngang hàng, và ngữ cảnh bắc cầu chỉ để hiển thị: các mount mà một lớp ngang hàng khai báo không bao giờ được giải, được lập index, hay được định tuyến nếu không có một pin trực tiếp trong lớp chủ. name của mỗi mount MUST là duy nhất trong manifest của lớp chủ và MUST NOT dùng lại chính name của lớp ngữ cảnh chủ. Vì không có gì đi xa hơn các lớp ngang hàng mà một lớp ngữ cảnh đã khai báo, nên các hình thoi và các chu trình đều trơ: A mount BC trong khi B cũng mount C thì đó là ba quan hệ trực tiếp, không phải một đồ thị để đi.

Các lớp ngữ cảnh đã mount là những nguồn riêng biệt, có tên, không bị trộn vào các danh mục của lớp chủ. Lớp ngữ cảnh của chính lớp chủ mới có thẩm quyền đối với kho mã nguồn của lớp chủ; mỗi lớp ngang hàng có thẩm quyền đối với chính nó. Không có một không gian tên chung toàn tổ chức, nên cũng không có thứ tự ưu tiên nào giữa các lớp ngang hàng phải giải quyết: một agent nạp đúng lát cắt nó cần từ đúng lớp ngữ cảnh sở hữu lát cắt ấy, và nêu tên lớp đó. Và nội dung đã mount là đầu vào không đáng tin: đó là ngữ cảnh để đọc, không bao giờ là chỉ thị để thi hành. Văn xuôi của một lớp ngang hàng có thể chứa sai sót hoặc chỉ thị bị tiêm vào, y như mọi bề mặt đọc khác, nên một agent coi nó là tài liệu để cân nhắc và trích dẫn, áp tư thế của chính lớp chủ lên hành động của mình, và không bao giờ tuân theo văn bản mệnh lệnh tìm thấy trong một mount như thể đó là chỉ thị của lớp chủ.

Việc báo cáo pin lỗi thời là có nhận biết quan hệ tổ tiên và trung thực về những gì nó nhìn được: công cụ so pin với ref đối chiếu (trackingRef, hoặc nhánh mặc định được nguồn quảng bá) và báo cáo là đã cập nhật, tụt lại N bước, đi trước, đã rẽ nhánh, hay không liên quan, luôn nêu tên ref được đem so, loại kho mã nguồn mà phép so đã chạy trong đó (kho mã nguồn do resolver quản lý, một gợi ý cục bộ trên máy, hay một submodule của lớp chủ), việc ref đối chiếu là ref của chính resolver hay là một ref nó không sở hữu, thời điểm quan sát, và việc phần tổ tiên có đầy đủ hay không. Khi không với tới được kho đối tượng nào thì báo cáo là unknown, không bao giờ là một phỏng đoán. Một pin chỉ giải được nhờ một gợi ý cục bộ trên máy thì xác lập tính sẵn có, chứ không xác lập mức tuân thủ: ở mức federated, pin MUST với tới được từ một ref được quảng bá của source (xem conformance.md), và một phép kiểm không với tới được nguồn sẽ báo unknown, và unknown thì không bao giờ trao mức.

Một lớp ngữ cảnh chỉ đạt mức tuân thủ federated khi những quan hệ này là có thật và kiểm được: lớp ngữ cảnh được ít nhất một kho mã nguồn khác tiêu thụ dưới dạng một mount có ghim, việc báo cáo pin lỗi thời đã có sẵn, và mọi mount đã khai đều mang một khai báo có ghim đầy đủ (nguồn, pin là commit đầy đủ, siêu dữ liệu định tuyến) với quyền sở hữu còn nguyên vẹn (xem conformance.md). SDK tham chiếu kiểm các phần máy kiểm được và báo cáo các vấn đề; trạng thái hiện thực hoá cố ý không phải một đầu vào của việc xét tuân thủ, vì tính sẵn có trên một cái máy chẳng nói lên điều gì về tính đúng đắn của lời khai.

Một manifest mẫu đầy đủ cho hình hài này nằm ở examples/multi-repo/.

Vòng tròn ghép quyền sở hữu lại; nó không tập trung hoá quyền sở hữu. Một monorepo là vòng tròn người và agent của một nhóm cùng đọc một lớp ngữ cảnh; một tổ chức nhiều kho mã nguồn là một vòng tròn của những vòng tròn ấy, mỗi vòng vẫn thuộc về những người giữ cho nó đúng.

Đọc một lớp ngữ cảnh federation#

Việc khám phá là trách nhiệm của lớp chủ phải làm cho rõ ràng, không phải việc của agent phải suy đoán. Một lớp chủ có khai báo mount sẽ làm chúng nổi lên ở đúng hai chỗ mà agent vốn đã đọc: boot profile nêu tên các lớp ngang hàng bằng ngôn ngữ tác vụ (theo boot-profile.md), và context index được sinh ra mang một mảng định tuyến mounts (theo machine-readable-surface.md). Một agent không bao giờ phải đọc manifest để tìm ra một lớp ngang hàng.

Khi đọc một lớp chủ có federation, một agent:

  1. Nạp boot profile của lớp chủ và bề mặt máy đọc được của lớp chủ trước; lớp ngữ cảnh của chính lớp chủ có thẩm quyền đối với kho mã nguồn của lớp chủ.
  2. Đọc các bản ghi định tuyến mount mà lớp chủ phơi ra, trước khi chốt phạm vi ngữ cảnh của tác vụ. Một mount là bắt buộc cho tác vụ khi boot profile của lớp chủ, một bản ghi mount trong index, hoặc siêu dữ liệu requiredWhen của mount nói rằng tác vụ cần tới nó; một mount là liên quan tới tác vụ khi, theo thuật toán Định tuyến tác vụ (machine-readable-surface.md), ít nhất một trong các categories của nó khớp một danh mục được phát tín hiệu của tác vụ, hoặc ít nhất một trong các topics của nó khớp chính xác một chủ đề mà tác vụ nêu tên tường minh. Một lần khớp chủ đề chỉ chọn ra mount mà thôi: nó không nở ra một danh mục nào và không chọn nội dung nào bên trong lớp ngang hàng. Chính thuật toán ấy sau đó định tuyến lát cắt mà agent nạp từ index của lớp ngang hàng.
  3. Để nạp một lớp ngang hàng liên quan tới tác vụ, lấy vị trí của phép chiếu đã nạp về từ trạng thái của resolver (lệnh mounts locate của SDK tham chiếu; không bao giờ bằng cách suy ra đường dẫn bộ nhớ đệm), đọc leji.json của lớp ngang hàng ở đó, xác minh rằng name của lớp ngang hàng khớp với lời khai của lớp chủ, đọc boot profile của lớp ngang hàng, rồi chỉ nạp đúng lát cắt mà tác vụ cần từ index của chính lớp ngang hàng. Các dữ kiện, ràng buộc và trích dẫn đều mang theo tên của lớp ngữ cảnh mà chúng đến từ đó, và nội dung đã mount vẫn là đầu vào không đáng tin theo các quy tắc của khuôn mẫu này.
  4. MUST NOT đệ quy vào chính federation.mounts của một lớp ngang hàng. Nếu một lớp ngữ cảnh cháu thực sự cần cho các tác vụ của lớp chủ, thì lớp chủ MUST khai báo nó thành một mount trực tiếp của riêng mình.
  5. Áp tư thế theo quyền sở hữu: tư thế của lớp chủ chi phối công việc trong kho mã nguồn của lớp chủ, còn tư thế của một lớp ngang hàng chi phối cách diễn giải nội dung của lớp ngang hàng đó và cách đề xuất thay đổi cho nó. Ở nơi hướng dẫn của lớp chủ và của lớp ngang hàng xung đột trong cùng một tác vụ mà không rõ lớp ngữ cảnh nào sở hữu phần đó, agent MUST dừng lại và hỏi chứ không tự chọn một thứ tự ưu tiên chưa từng được nói ra.

Công cụ MAY cung cấp các hàm trợ giúp nạp có nhận biết mount, nhưng việc đọc các lớp ngang hàng không đòi hỏi phải có công cụ Leji: đọc thô từ kho mã nguồn vẫn là tuân thủ khi nó đi theo đúng quy trình này và giữ nguyên các ranh giới truy cập.

Mount bị giới hạn#

Federation vượt qua một ranh giới truy cập khi các lớp được ghép lại có những nhóm đối tượng khác nhau (xem governance.md). Quyền truy cập vẫn là việc của hệ quản lý phiên bản phải cưỡng chế: một người đọc hoặc là giải được kho mã nguồn của một mount, hoặc là không. Việc của bản đặc tả là giữ cho ranh giới đó không rò rỉ và không hỏng một cách âm thầm.

  1. Một lớp bị giới hạn MUST NOT được khai báo thành một mount trong một lớp chủ có nhóm đối tượng rộng hơn nhóm của chính lớp bị giới hạn ấy: mọi bên tham gia mà lớp chủ cho vào đều phải vốn đã được cho vào lớp được mount. Bản thân lời khai mount (sự hiện diện của nó, cùng name, owner, role, categories, topics, requiredWhen, source, pin, và trackingRef) MUST NOT tiết lộ bất cứ điều gì mà nhóm đối tượng của lớp chủ không được phép thấy. Ở nơi một nhóm đối tượng rộng hơn cần tới một quyết định bị giới hạn, hãy công bố một lớp đồng hành đã che thông tin hoặc một bản tóm tắt quyết định công khai, chứ không phải một mount tới lớp bị giới hạn.

  2. Một mount là một tham chiếu, không phải một sự cấp quyền. Việc khai báo một mount không bao giờ mở rộng phạm vi ai được đọc lớp đã mount vượt quá những gì hệ quản lý phiên bản vốn đã cho phép; việc một người đọc cụ thể có giải được nó hay không là do bên đó quyết định, không phải do manifest của lớp chủ.

  3. Đóng khi lỗi, không bao giờ âm thầm. Một người đọc không giải được một mount bắt buộc cho tác vụ (theo định nghĩa ở Đọc một lớp ngữ cảnh federation) MUST dừng lại và báo rằng ngữ cảnh không đầy đủ. Nó MUST NOT đi tiếp như thể lớp không truy cập được kia không tồn tại: một agent hành động trên phần ngữ cảnh không đầy đủ mà nó không nhìn thấy chính là kiểu hỏng mà quy tắc này sinh ra để ngăn. Chiều ngược lại cũng là một kiểu hỏng: một người đọc giải được một lớp ngang hàng bắt buộc hay liên quan tới tác vụ nhưng vẫn bỏ qua nó thì đang hành động trên ngữ cảnh không đầy đủ một cách âm thầm, và như vậy là không tuân thủ.

    Công cụ chống lưng được và không chống lưng được những gì ở đây: một bộ kiểm tra báo cáo tính sẵn có tại chỗ (một mount đã khai mà ở đây không có phép chiếu nào được nạp về), và thuật toán định tuyến quyết định mức liên quan tới tác vụ theo phần chồng lấn về danh mục và chủ đề (các SDK tham chiếu làm nổi lên những mount liên quan tới tác vụ; xem machine-readable-surface.md, phần Định tuyến tác vụ). Nhưng tính bắt buộc đối với tác vụ thì xoay quanh requiredWhen, vốn là những điều kiện tác vụ viết bằng văn bản tự do, còn khả năng với tới lúc chạy thì xoay quanh chính quyền truy cập của người đọc tại thời điểm đọc; cả hai đều là việc agent phải phán đoán, không phải việc của một công cụ. Do vậy, yêu cầu MUST về việc đóng khi lỗi là do agent tự khai: công cụ làm nổi lên những gì nó nhìn được, còn agent thì cưỡng chế việc dừng lại.

Ghi chú (tham khảo)#

Khuôn mẫu submodule mang tiếng xấu chủ yếu vì những submodule chứa code và gắn với bước build. Một nhánh lá chỉ chứa tài liệu không gặp các kiểu hỏng đó: không có gì được biên dịch dựa trên nó, không có gì bị vỡ khi nó tụt lại, còn pin chỉ ghi nhận “kho mã nguồn này đang làm việc từ phiên bản nào của sự thật”. Đó là thông tin, không phải rủi ro.

Federation có vẻ gồm nhiều bộ phận vận hành hơn việc gộp chung, nhưng thực tế lại ít hơn. Gộp chỉ rẻ ở lần đầu rồi trở nên đắt đỏ mãi về sau: mọi sửa đổi liên nhóm đều phải qua chủ sở hữu kho mã nguồn trung tâm, còn những phần không được nhóm nào đọc hằng ngày sẽ dần xuống cấp. Các mount ngang hàng giữ từng lớp ngữ cảnh nhỏ gọn, có chủ và được đọc thường xuyên; chi phí chỉ là một lần cập nhật pin dưới dạng diff có thể xem xét, không phải một cuộc họp.

Mức tuân thủ ↗pull request

Mức tuân thủ

Việc áp dụng theo từng phần là chủ ý thiết kế. Có bốn mức, mỗi mức sau bao hàm mức trước; nhóm tuyên bố mức của mình trong manifest (conformance.claimedLevel). Mọi tuyên bố đều là tự khai: không có chương trình chứng nhận nào.

Mức tuân thủ được đánh giá trên lớp ngữ cảnh đúng như nó hiện ra ở nơi phép kiểm chạy, chứ không phải trên một lớp chính thống nào đó mà một bản sao có thể đang đại diện. Một bản sao tới được mà không kèm kho mã nguồn của nó thì được đọc ở chế độ suy giảm theo context-layer.md, và đọc ở chế độ suy giảm không bao giờ là con đường dẫn tới thẩm quyền chính thống: một bản sao như vậy không xác minh được, và công cụ sẽ nói thẳng ra điều đó chứ không để câu hỏi lửng lơ.

Phần lớn các mục trong danh sách kiểm đều máy xác minh được: công cụ tham chiếu kiểm tra chúng trên lớp ngữ cảnh và bác bỏ tuyên bố không đáp ứng yêu cầu. Có bốn kết quả báo cáo, và chúng được chủ ý quy định là không thể dùng thay cho nhau:

  • fail: bằng chứng đã được thu thập và yêu cầu không được đáp ứng.
  • (khai theo quy trình), báo cáo là manual: mục này mô tả một thực hành của nhóm (một cổng xem xét, một job CI, một bên tiêu thụ bên ngoài) mà không công cụ nào xác nhận được nếu chỉ nhìn vào kho mã nguồn, nên nhóm tự đứng ra bảo đảm. Chỉ những mục được gắn nhãn (khai theo quy trình) bên dưới mới bao giờ được báo cáo theo cách này.
  • unknown: một mục máy kiểm mà bằng chứng không lấy được trong lần chạy này, chẳng hạn phép kiểm khả năng với tới pin trong federation khi không có quyền truy cập nguồn, hoặc kỷ luật chỉ thêm mới khi không có mốc git nào để so sánh. unknown không bao giờ trao một mức, và cũng không bao giờ bác bỏ một tuyên bố mà một lần chạy có bằng chứng có thể xác nhận được.
  • not applicable: một mục máy kiểm có điều kiện nhưng không áp cho lớp ngữ cảnh này, chẳng hạn các mục về mount trong federation trên một lớp không khai báo mount nào. Nó không được chấm điểm, và cũng không phải bằng chứng theo chiều nào cả.

verifiedLevel mà công cụ báo cáo là mức cao nhất mà mọi mục máy xác minh được có áp dụng đều đạt, và không bao giờ cao hơn mức mà lớp ngữ cảnh tuyên bố; cả fail lẫn unknown đều chặn việc trao mức, còn những mục khai theo quy trình hoặc không áp dụng thì không được chấm. Việc chặn trần theo tuyên bố là có chủ ý: việc xác minh trả lời câu hỏi tuyên bố có đứng vững hay không, chứ không phải lớp ngữ cảnh này lẽ ra có thể tuyên bố tới đâu, nên một lớp tuyên bố core mà bằng chứng đủ đưa nó lên governed thì vẫn được báo là core, và cách để nâng mức được báo cáo là nâng chính tuyên bố. verifiedLevel không bao giờ khẳng định thay cho các mục khai theo quy trình, nên một verifiedLevel đạt là điều kiện cần chứ chưa đủ cho một mức có mang những mục đó. Mọi mục bên dưới đều là máy xác minh được, trừ khi được gắn nhãn (khai theo quy trình).

Hai mục máy xác minh được hành xử khác đi trên một bản sao suy giảm, và khác biệt đó là hệ quả của việc mỗi mục có bằng chứng gì. Sự hiện diện của git thì có câu trả lời: một bản sao không nằm trong một kho git thì không đáp ứng yêu cầu ở mức core rằng lớp ngữ cảnh phải nằm trong một kho mã nguồn như vậy, nên mục đó là fail. Kỷ luật chỉ thêm mới của changelog thì không có câu trả lời: tệp có thể hoàn toàn đúng khuôn dạng trong khi trạng thái đã commit trước đó, thứ cần để đối chiếu, lại không với tới được, nên mục đó là unknown và lớp ngữ cảnh đơn giản là không xác minh được ở mức indexed từ bản sao ấy. Cả hai mục này đều không được báo là manual, vì nhãn đó dành riêng cho các mục đã gắn nhãn khai theo quy trình. Ngoài ra, quy tắc về độ tươi dành cho người đọc (làm nổi ngữ cảnh lỗi thời đã nạp, và dừng lại hoặc hỏi khi một mục bắt buộc đã hết hạn, theo governance.md) là quy tắc về hành vi của người đọc, không phải một cổng chặn mức tuân thủ: lệnh leji route tham chiếu đóng dấu lên mỗi tài liệu đã định tuyến hạn xem xét lại và thời điểm hết hạn của nó, để một agent áp dụng được quy tắc ấy.

Có ba mục hôm nay được xác minh ở độ sâu thấp hơn chủ ý đã nêu của chúng, và khoảng cách đó được nói thẳng ở đây thay vì để người đọc tự phát hiện. Mục về boot profile được xác minh ở mức sự hiện diện tại đường dẫn đã khai báo và ở mức có các tiêu đề danh tính, phần nạp và tư thế (mọi lần chạy validate đều báo một tiêu đề còn thiếu dưới dạng cảnh báo boot-profile-sections, chứ không phải cổng chặn), còn việc phần danh tính có nói được điều gì thực chất hay không thì đi nhờ phép lint --content tuỳ chọn, thứ cũng chỉ ra nội dung mẫu còn sót ở bất kỳ chỗ nào trong profile. Mục về quyết định thật được xác minh ở mức frontmatter hợp lệ theo schema trên ít nhất một bản ghi giải được; phần nội dung thực chất của thân bài (một quyết định thật, không phải một khung rỗng) cũng đi nhờ --content. Mục về changelog là mục thứ ba: kỷ luật chỉ thêm mới được kiểm so với trạng thái của tệp ở HEAD, tức là bắt được một lần viết lại còn nằm trong cây làm việc, đúng trường hợp mà một hook pre-commit sinh ra để phục vụ. Trong một bản checkout của hệ tích hợp liên tục thì cây làm việc chính là HEAD, nên một lần viết lại đã commit sẵn thì phép kiểm không nhìn thấy, và việc xem xét bộ thay đổi mới là thứ phủ được chỗ đó. Do vậy mục này xác minh cây làm việc, chứ không xác minh lịch sử. Chủ ý đã nêu trong cả ba mục vẫn mang tính chuẩn tắc về những gì một lớp ngữ cảnh tuân thủ phải có; việc đào sâu các phép kiểm bằng máy, và việc đối chiếu changelog với một phiên bản gốc tường minh, đều nằm trong lộ trình của công cụ tham chiếu. Việc xác minh mức federated còn đòi hỏi thêm ít nhất một mục federation.mounts được khai báo: một lớp ngữ cảnh chỉ đóng vai nhà cung cấp (được các kho mã nguồn khác tiêu thụ nhưng không tự khai báo mount nào) thì xác minh được ở governed, và tư cách federation của nó dựa trên các mục về tiêu thụ được khai theo quy trình.

Mức 1: core#

Một lớp ngữ cảnh tồn tại, và cả người lẫn agent đều làm việc được từ nó.

  • Lớp ngữ cảnh nằm trong một kho git, được đánh phiên bản cùng với phần công việc mà nó mô tả (theo context-layer.md, phần Yêu cầu).
  • leji.json ở gốc kho mã nguồn, hợp lệ theo schema của manifest.
  • Có một boot profile ở đường dẫn đã khai báo, bao gồm danh tính, phần nạp và tư thế.
  • Ít nhất domain hoặc system được ánh xạ (qua tệp index của nó) và có nội dung, với ít nhất một tài liệu chủ ý giải được (chỉ toàn bản ghi thì không mang theo ngữ cảnh vận hành nào), cộng thêm decisions với ít nhất một bản ghi quyết định thật: một bản ghi mang status cụ thể và một quyết định thật trong phần thân, không phải một khung rỗng hay nội dung mẫu.
  • Có một chủ sở hữu chính được nêu tên.
  • Các tệp điểm vào của nhà cung cấp, nếu có, đều chuyển hướng tới boot profile.

Mức 2: indexed#

Lớp ngữ cảnh đọc được đối với công cụ.

  • Toàn bộ mức core.
  • Có một context index được sinh ra, còn khớp với cây tệp.
  • Có một changelog máy đọc được; các thay đổi của lớp ngữ cảnh đều ghi thêm mục vào đó.

Mức 3: governed#

Việc cưỡng chế nằm trong cơ chế vận hành, không dựa vào thiện chí.

  • Toàn bộ mức indexed.
  • Thay đổi của lớp ngữ cảnh đi qua cổng xem xét của kho mã nguồn; người phê duyệt. (khai theo quy trình)
  • Có agent profile (ít nhất một profile có role: core) hợp lệ theo schema của profile.
  • CI kiểm bề mặt này: manifest, index khớp với cây tệp, kỷ luật changelog, frontmatter của profile, và các đường dẫn đã khai báo đều giải được. (khai theo quy trình)
  • Hạn tươi được khai báo và được kiểm (chỉ báo cáo cũng chấp nhận được).

Mức 4: federated#

Lớp ngữ cảnh trải rộng trên một tổ chức nhiều kho mã nguồn.

  • Toàn bộ mức governed.
  • Lớp ngữ cảnh được ít nhất một kho mã nguồn khác tiêu thụ dưới dạng một mount có ghim, và các lần cập nhật pin đến dưới dạng những bộ thay đổi xem xét được. (khai theo quy trình)
  • Có sẵn cơ chế báo cáo pin lỗi thời: bên tiêu thụ thấy được pin của mình đang tụt lại bao xa so với ref đối chiếu. Báo cáo có nhận biết quan hệ tổ tiên của SDK tham chiếu phủ được các mount federation đã khai báo; phần báo cáo ở phía tiêu thụ ngoài đó ra là việc của nhóm. (khai theo quy trình)
  • Mọi lớp ngữ cảnh ngang hàng đều được khai báo dưới dạng mount có ghim đầy đủ theo distribution.md: một source đã chuẩn hoá và một pin là commit đầy đủ, với quyền sở hữu còn nguyên vẹn. Trạng thái hiện thực hoá trên một máy cụ thể nào đó không phải đầu vào của việc xét tuân thủ.
  • Pin của mỗi mount đã khai báo đều với tới được từ một ref được quảng bá của source tương ứng (trackingRef đã khai báo, hoặc nhánh mặc định của nguồn). Phép kiểm này cần quyền truy cập nguồn: không có nó thì kết quả là unknown, và unknown không bao giờ trao mức. Một pin chỉ giải được nhờ một gợi ý cục bộ trên máy xác lập tính sẵn có, không phải mức tuân thủ.
  • Mỗi mount đã khai báo đều mang siêu dữ liệu định tuyến: ít nhất categories, cộng thêm topics hoặc requiredWhen, để một agent quyết định được mức liên quan mà không phải đọc lớp ngang hàng.
  • Boot profile làm nổi lên mọi lớp ngang hàng đã mount, và index được sinh ra mang mảng định tuyến mounts, để một agent khám phá và nạp được các lớp ngang hàng mà không phải đọc manifest (theo boot-profile.md, machine-readable-surface.md).

Ghi chú (tham khảo)#

core là mức tối thiểu khiến một lớp ngữ cảnh trở nên có thật, indexed thêm vào bề mặt được sinh ra mà công cụ đọc, governed là chỗ lớp ngữ cảnh thôi phụ thuộc vào kỷ luật của bất kỳ ai, còn federated dành cho những tổ chức nơi đã có hơn một nhóm sở hữu một lớp ngữ cảnh đáng giữ nguyên vẹn. Phần lớn các nhóm nên đi tới governed rồi dừng lại; federated tồn tại cho những tổ chức kia, chứ không phải như một huy hiệu trưởng thành.

Đánh phiên bản ↗pull request

Đánh phiên bản

Ba thành phần được đánh phiên bản độc lập: bản đặc tả, các schema và mọi công cụ hiện thực bản đặc tả.

Bản đặc tả#

  1. Bản đặc tả mang một phiên bản SemVer (hiện tại là 1.0.0). Thay đổi phá vỡ tương thích đòi hỏi một phiên bản major; mọi thay đổi đều được ghi vào changelog của kho mã nguồn.
  2. Một lớp ngữ cảnh khai báo nhánh đặc tả mà nó nhắm tới trong leji.json qua khoá tự đặt tên leji (ví dụ "leji": "1.0"), theo quy ước của OpenAPI. Giá trị này là nhánh đặc tả (major.minor), không bao giờ là phiên bản patch: một bản patch (từ 1.0.0 lên 1.0.1) tinh chỉnh câu chữ hoặc công cụ mà không dịch chuyển nhánh, nên manifest vẫn giữ "1.0" qua mọi bản patch. Công cụ MUST kiểm tra một lớp ngữ cảnh theo đúng nhánh được khai báo, chứ không theo nhánh mới nhất.

Nhánh preview#

Một nhánh đặc tả MAY được chỉ định là preview. Nhánh preview có thể sửa đổi tại chỗ: nó MAY thay đổi theo những cách vốn sẽ là phá vỡ tương thích, thay vì phải nâng lên một phiên bản mới, cho tới khi được đóng băng ở thời điểm phát hành chính thức (GA). Quy tắc “thay đổi phá vỡ tương thích đòi hỏi một phiên bản major” (mục 1) và quy tắc “$id dịch chuyển khi hình thái thay đổi không tương thích” (mục 3) chỉ có hiệu lực từ lúc đóng băng ở GA trở đi, chứ không áp dụng khi nhánh còn ở preview. Tại GA, nhánh được đóng băng và cả hai quy tắc bắt đầu có hiệu lực.

Một nhánh phát hành trước khi có bản chính thức MUST khai báo điều đó ngay ở lần phát hành đầu tiên.

Nhánh 1.0 đã đóng băng ở bản phát hành công cụ tham chiếu v1.3.0. Trong nhánh này, thay đổi schema chỉ được phép là thêm mới và $id vẫn ở v1.0; mọi thay đổi không tương thích đều phát hành thành một nhánh mới, không bao giờ sửa tại chỗ.

Các schema#

  1. Mỗi schema mang một $id ổn định theo dạng https://leji.org/schemas/v<major>.<minor>/<name>.schema.json. Dòng $id chỉ dịch chuyển khi hình thái của schema thay đổi không tương thích.
  2. Trong một nhánh đã phát hành, thay đổi schema MUST chỉ là thêm mới (thêm trường tuỳ chọn). Việc bỏ trường hoặc đổi ngữ nghĩa đòi hỏi một nhánh mới.
  3. Các artifact máy đọc được khác ngoài manifest khai báo nhánh schema mà chúng được viết theo, qua schemaVersion; manifest khai báo nhánh đặc tả mà nó nhắm tới qua khoá tự đặt tên leji (mục 2).

Tập ổn định#

Những thứ sau được đóng băng trong một nhánh đặc tả; công cụ (kể cả các bản hiện thực thương mại sau này) xây dựng dựa trên chúng mà không cần một schema song song:

  • hình thái của manifest và tên tệp cố định leji.json,
  • các định danh danh mục (domain, system, practice, governance, decisions),
  • các định danh mức tuân thủ (core, indexed, governed, federated),
  • các quy tắc chuẩn hoá định danh và đường dẫn theo machine-readable-surface.md,
  • hình thái của mục index, mục changelog, agent profile và bản ghi quyết định.

Công cụ hiện thực (tham khảo)#

Các SDK và CLI dùng phiên bản SemVer riêng, đồng thời khai báo những nhánh đặc tả mà chúng hỗ trợ. Các SDK tham chiếu trong kho mã nguồn này gồm gói npm @leji-org/leji (packages/sdk), gói PyPI leji (packages/sdk-py) và Go module leji (packages/sdk-go, một binary tĩnh duy nhất); chúng có hành vi giống hệt nhau và được kiểm tra bằng cùng một bộ fixture dùng chung.