Skip to content

feat(meta): config-driven L9_META pipeline, schema v2, full-repo stamp - #147

Closed
cryptoxdog wants to merge 4 commits into
mainfrom
feat/l9-meta-pipeline
Closed

feat(meta): config-driven L9_META pipeline, schema v2, full-repo stamp#147
cryptoxdog wants to merge 4 commits into
mainfrom
feat/l9-meta-pipeline

Conversation

@cryptoxdog

Copy link
Copy Markdown
Collaborator

Why

The injector kept a hardcoded FILE_REGISTRY as its source of truth, and two of its five formatters were stubs that emitted a delimiter pair with no field lines. So --apply on any YAML, shell, Makefile, or Python path wrote an empty block, and the registry drifted from the tree with nothing to catch it: 10 entries pointed at files that no longer existed, while ~500 tracked files were invisible to it. Contract C-018 claimed every file carries a header; in practice 371 did, inconsistently.

What changed

Config replaces the registry. l9-meta.yaml resolves header values from path rules rather than per-file entries: defaults -> rules (last match wins) -> overrides[exact path]. Adding a file no longer means editing the tool.

Formatters restored and hardened. tools/l9_meta/formats/ is one module per format. Beyond emitting the field lines again:

  • legacy bare-form # L9_META blocks are recognized and replaced, not duplicated
  • markdown insertion is front-matter aware — the header goes after the front matter, never on line 1, so all 16 front-matter files still parse
  • detection is anchored at column 0 and bounded to the leading comment region, so an indented example in a docstring or an L9_META literal inside a test fixture is never read as a header

Discovery is git-based. Enumeration is git ls-files -z, so paths with spaces and non-ASCII names survive, and untracked or generated output cannot be stamped.

Tags become a closed taxonomy. Three facets — family (exactly 1), capability (0-2), concern (0-2) — with a min_files floor. The corpus had 343 distinct tags across 371 files: 197 used exactly once and 41 that merely restated layer. Neither is queryable. Folded to a vocabulary of 188.

Schema v2 drops owner, which had no consumer. The parser still accepts v1; the writer emits v2.

Unknown config keys are rejected. A misspelled vocabulary key silently disables the constraint it declares, and check would then pass while enforcing nothing.

Enforcement

  • l9-meta-check pre-commit hook (always_run, since a rule edit changes expectations for files that are not themselves staged)
  • meta-headers CI job in contracts.yml, which test now depends on
  • C-018 moves from SHOULD over engine/ + chassis/ Python to schema v2 over every tracked file

tools/l9_meta_injector.py remains as a shim over tools.l9_meta.cli, so the six docs and hooks that invoke it by path keep working. l9-meta init scaffolds a config in a fresh repo.

Deferred

**/*.json is excluded and tracked as DEFERRED-003. JSON injection round-trips through the parser, so stamping also reserializes: packet-envelope.schema.json came back +159/−29 for a 9-line header. Equivalent JSON, but churn unrelated to metadata. 16 files.

Test plan

  • check reports 607/607 consistent (0 missing, 0 drift, 0 misplaced, 0 invalid)
  • second apply writes 0 files — idempotent
  • 1416 unit + contract tests pass
  • ruff check . clean; ruff format --check . — 344 files already formatted
  • all 344 Python, 92 YAML and 16 front-matter files still parse; from __future__ stays the first statement
  • contract_scanner and verify_contracts unchanged
  • round-trip (render -> parse -> render) and idempotency (inject(inject(x)) == inject(x)) fixtures per format

Branched from origin/main in a clean worktree, so the diff is only the meta pipeline plus the headers it writes.

Known pre-existing on main, not touched here: mypy reports a syntax error in engine/inference_rule_registry.py.

Made with Cursor

cryptoxdog and others added 3 commits July 24, 2026 17:53
The injector had a hardcoded FILE_REGISTRY as its source of truth, and two of
its five formatters were stubs that emitted a delimiter pair with no field
lines. So `--apply` on any YAML, shell, Makefile, or Python path wrote an empty
block, and the registry drifted from the tree with no way to notice: 10 entries
pointed at files that no longer existed while ~500 tracked files were invisible
to it.

Replace the registry with l9-meta.yaml, which resolves header values from path
rules instead of per-file entries, and make the whole thing verifiable:

- tools/l9_meta/ — model, discovery, config, resolve, adopt, emit, cli, and one
  module per format. tools/l9_meta_injector.py stays as a shim so existing docs
  and hooks that invoke it by path keep working.
- Restore the comment and docstring formatters; add legacy bare-form `# L9_META`
  stripping, front-matter-aware markdown insertion (header goes after the
  front matter, never on line 1), and delimiter-anchored detection so prose
  that merely mentions L9_META is never matched as a header.
- Discovery enumerates `git ls-files -z`, so paths with spaces and non-ASCII
  names survive, and untracked or generated output cannot be stamped.
- Tags become three facets — family (exactly 1), capability (0-2), concern
  (0-2) — with a min_files floor. This folds 343 ad-hoc tags to a vocabulary of
  188, dropping single-use tags and ones that only restated `layer`.
- Schema v2 drops `owner`, which had no consumer. The parser still accepts v1.
- Unknown keys in l9-meta.yaml are rejected rather than ignored: a misspelled
  vocabulary key silently disables the constraint it declares, and `check` would
  then pass while enforcing nothing.

Stamp all 607 eligible files, and enforce via an l9-meta-check pre-commit hook
and a meta-headers CI job that `test` now depends on. Contract C-018 moves from
SHOULD over engine+chassis Python to schema v2 over every tracked file.

Verified: check reports 607/607 consistent and a second apply writes 0 files
(idempotent); 1412 unit+contract tests pass; ruff check and ruff format --check
clean; contract_scanner and verify_contracts unchanged; all 16 JSON, 344 Python,
92 YAML and 16 front-matter files still parse.

Co-authored-by: Cursor <cursoragent@cursor.com>
Three files in the pipeline itself were reported INVALID, and the sixteen
tools/l9_meta modules were reported MISSING, immediately after the previous
commit — the tool could not stamp itself.

Two distinct false positives:

- Delimiters were matched with optional leading whitespace, so the indented
  `--- L9_META ---` example inside comment.py's and docstring.py's own
  docstrings parsed as a header. Since the surrounding prose has no field
  lines, both files reported malformed. The block is always emitted flush-left,
  so anchoring at column 0 makes indentation the discriminator between an
  example and a header.
- The comment finders scanned the whole file, so the `# --- L9_META ---` inside
  a fixture string in test_l9_meta_formats.py was read as that file's header.
  Bound the search to the leading comment region, which is the only place a
  header can legally appear.

Add a rule for tools/l9_meta/ and an override for l9-meta.yaml so the pipeline
and its config resolve like everything else, and stamp the sixteen modules.

Verified: check reports 623/623 consistent, a second apply writes 0 files,
1416 unit+contract tests pass, ruff check and ruff format --check clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
JSON injection round-trips through the parser, so stamping a file also
reserializes it. packet-envelope.schema.json came back +159/-29 for a 9-line
header and devcontainer.json lost its section spacing and had its compact
arrays exploded one element per line. The result is equivalent JSON, but the
churn is unrelated to metadata and would dominate review.

Exclude `**/*.json` and record DEFERRED-003 for the textual insert. The
formatter and its fixtures stay — only the corpus shrinks, from 623 files to
607.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown

Too Many Reviewable Files Changed
Changed: 607 files
Limit: 50 files
Action Required: Split into multiple focused PRs

PR Too Large
Reviewable lines changed: 8779
Limit: 1000 lines
Action Required: Break into smaller, atomic PRs

📋 Best Practices for Large Changes

  1. Refactoring + Features: Separate into 2 PRs
  2. Multiple Features: One PR per feature
  3. Database + Code: Separate migration from logic
  4. Generated Code: Exclude it from reviewable-size accounting

🚫 This PR is blocked until reviewable size limits are met.

@@ -1,3 +1,11 @@
# --- L9_META ---
steps:
# fetch-depth is irrelevant here, but the checkout must be a real git repo:
# discovery enumerates via `git ls-files -z`, not a filesystem walk.
- uses: actions/checkout@v6
# fetch-depth is irrelevant here, but the checkout must be a real git repo:
# discovery enumerates via `git ls-files -z`, not a filesystem walk.
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install pyyaml
- run: python tools/l9_meta_injector.py check
class Format(Protocol):
"""The four operations every format implements."""

def render(self, rec: MetaRecord) -> Any: ...
"""The four operations every format implements."""

def render(self, rec: MetaRecord) -> Any: ...
def parse(self, text: str) -> MetaRecord | None: ...

def render(self, rec: MetaRecord) -> Any: ...
def parse(self, text: str) -> MetaRecord | None: ...
def strip(self, text: str) -> str: ...
def render(self, rec: MetaRecord) -> Any: ...
def parse(self, text: str) -> MetaRecord | None: ...
def strip(self, text: str) -> str: ...
def inject(self, text: str, rec: MetaRecord) -> str: ...
cryptoxdog added a commit that referenced this pull request Jul 24, 2026
The four files were last written on this branch before the L9_META pipeline and
contract-registry work landed, so the resume state pointed at PRs that have since
been opened and at next actions that are done.

activeContext.md is rewritten rather than appended: a session-end hook had
overwritten it in the main working tree with a bare template ("No summary
provided" plus a generic file list), which would have destroyed the PR #144
narrative this branch already carried. The rewrite keeps that context, folds in
#145-#148, and records the one non-obvious hazard — PR #147 already contains the
full-repo header stamp, so a second "meta-only" PR would duplicate it.

progress.md, tasks.md and tech-debt.md are additive; the three tech-debt entries
that appear removed are replaced by updated versions in the same commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
cryptoxdog added a commit that referenced this pull request Jul 24, 2026
Records which Quantum-L9 CI repos are actually wired into this one
(l9-ci-core, l9-ci-sdk) versus referenced but not adopted (l9-harness,
l9-assurance), and the extend-never-replace rule for tools/audit_harness.py
and .github/workflows/audit.yml.

Without this written down the failure mode is silent: a future wiring pass
reads audit.yml, sees a thin caller, and replaces the repo's own harness
instead of extending it.

The AGENTS.md table entry that points here is part of a separate
docs stream and is not included, so the doc lands unreferenced for now.
Its header is schema v1; PR #147 moves the repo to v2 and will restamp it.

Co-authored-by: Cursor <cursoragent@cursor.com>
cryptoxdog added a commit that referenced this pull request Jul 25, 2026
The four files were last written on this branch before the L9_META pipeline and
contract-registry work landed, so the resume state pointed at PRs that have since
been opened and at next actions that are done.

activeContext.md is rewritten rather than appended: a session-end hook had
overwritten it in the main working tree with a bare template ("No summary
provided" plus a generic file list), which would have destroyed the PR #144
narrative this branch already carried. The rewrite keeps that context, folds in
#145-#148, and records the one non-obvious hazard — PR #147 already contains the
full-repo header stamp, so a second "meta-only" PR would duplicate it.

progress.md, tasks.md and tech-debt.md are additive; the three tech-debt entries
that appear removed are replaced by updated versions in the same commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
TROUBLESHOOTING now describes `l9_meta_injector.py apply` and the
resolve-by-path model; PRODUCTION-SPEED-PACK drops its Suite-6 header for
schema v2; DEFERRED records the JSON exclusion (DEFERRED-003).
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@cryptoxdog

Copy link
Copy Markdown
Collaborator Author

Closing: the L9_META v2 injection pipeline is stale. The 607-file schema-v2 stamp it carried has been discarded from the working tree; the full pre-discard state is preserved locally on safety ref 591b66c if any of it is wanted later. tools/l9_meta_injector.py remains on main in its original form.

@cryptoxdog cryptoxdog closed this Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants