quill: placeholder-ID format standard and verifier
This commit is contained in:
1 parent
13aa0c95d6
commit
37032460db
2 files changed
+362
No files matched your search
@@ -0,0 +1,79 @@
|
||||
PLACEHOLDER-ID FORMAT STANDARD v1.0
|
||||
=====================================
|
||||
|
||||
Purpose: eliminate the fault where example IDs that do not exist in the repo
|
||||
are cited as if they were real. This standard defines how to mark invented IDs
|
||||
and how to verify that every cited ID exists.
|
||||
|
||||
PROBLEM
|
||||
-------
|
||||
On 2026-10-13, Gemini's first reply cited example IDs (fleet-ops, timmy-dispatch,
|
||||
self_heal.py, ...) that do not exist in the repo and were not marked as
|
||||
placeholders. The fault is recorded in WIZARDS-TABLE.md.
|
||||
|
||||
RULES
|
||||
-----
|
||||
1. REAL IDs
|
||||
- A real ID is a `<kebab-case>` token or filename that exists in the repo
|
||||
at the time of citation.
|
||||
- Cite real IDs bare: `queue-triage`, `WORK-QUEUE.md`.
|
||||
|
||||
2. PLACEHOLDER IDs
|
||||
- A placeholder ID is an invented example used to illustrate a format.
|
||||
- Must be wrapped in angle brackets: `<my-example-id>`
|
||||
- OR prefixed with a marker: `[PLACEHOLDER: my-example-id]`
|
||||
- OR enclosed in a code span with the marker: `` `<PLACEHOLDER: my-example-id>` ``
|
||||
|
||||
3. FORBIDDEN
|
||||
- Bare invented `<kebab-case>` tokens that look like real IDs.
|
||||
- Bare invented filenames that look like real paths.
|
||||
- No exception for "obviously fake" — the verifier does not guess intent.
|
||||
|
||||
VERIFICATION
|
||||
------------
|
||||
Script: `verify_placeholder_ids.py`
|
||||
|
||||
Scope: all `.md` files under:
|
||||
- /root/.hermes/wizard-outputs/quill/
|
||||
- /root/troupe-standup/wizard-work/quill/
|
||||
|
||||
Method:
|
||||
- Extract tokens matching `[a-z0-9]+(-[a-z0-9]+)+` and filenames
|
||||
matching `[a-z0-9_-]+\.md` / `[a-z0-9_-]+\.py` / `[a-z0-9_-]+\.txt`
|
||||
- Cross-reference each token against actual filesystem entries in the repo.
|
||||
- A token is a violation if:
|
||||
(a) it does not exist as a file or directory in the repo, AND
|
||||
(b) it is NOT wrapped in `<...>`, AND
|
||||
(c) it is NOT prefixed `[PLACEHOLDER:` or `<PLACEHOLDER:`.
|
||||
|
||||
Output: list of violating file:line pairs, or zero violations.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
Before (violation):
|
||||
"See queue-triage.md and fleet-ops for details."
|
||||
|
||||
After (compliant):
|
||||
"See `queue-triage.md` and `<fleet-ops>` for details."
|
||||
|
||||
Before (violation):
|
||||
"IDs like timmy-dispatch or self_heal.py are reserved."
|
||||
|
||||
After (compliant):
|
||||
"IDs like `[PLACEHOLDER: timmy-dispatch]` or `<self_heal.py>` are reserved."
|
||||
|
||||
CHECKLIST
|
||||
---------
|
||||
[ ] Every bare kebab-case token in the draft exists in the repo filesystem.
|
||||
[ ] Every invented token is wrapped in `<...>` or marked `[PLACEHOLDER: ...]`.
|
||||
[ ] `verify_placeholder_ids.py` exits 0 against the draft itself.
|
||||
[ ] The script is run on the artifact before submission.
|
||||
|
||||
SOURCE OF TRUTH
|
||||
---------------
|
||||
/root/.hermes/wizard-outputs/quill/placeholder-id-format-standard.md
|
||||
Mirror:
|
||||
/root/troupe-standup/wizard-work/quill/placeholder-id-format-standard.md
|
||||
|
||||
Lane owner: quill (Draft — fast writing and drafts)
|
||||
Last updated: 2026-10-27
|
||||
Executable
+283
@@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env python3
|
||||
"""verify_placeholder_ids.py — verify the placeholder-ID standard and its own detection.
|
||||
|
||||
Scans:
|
||||
1. The standard document itself for bare invented IDs.
|
||||
2. Embedded unit tests that exercise the detection logic.
|
||||
|
||||
Exits 0 on clean pass; exits 1 on any failure.
|
||||
"""
|
||||
import re, os, sys
|
||||
|
||||
REPO_ROOT = "/root/troupe-standup"
|
||||
ROOTS = [
|
||||
"/root/.hermes/wizard-outputs/quill/",
|
||||
"/root/troupe-standup/wizard-work/quill/",
|
||||
]
|
||||
STANDARD = "/root/.hermes/wizard-outputs/quill/placeholder-id-format-standard.md"
|
||||
|
||||
# Build existing id set from filesystem (repo + scanned roots)
|
||||
existing_ids = set()
|
||||
for scan_root in [REPO_ROOT] + ROOTS:
|
||||
root_name = os.path.basename(scan_root.rstrip("/"))
|
||||
if root_name and not root_name.startswith("."):
|
||||
existing_ids.add(root_name)
|
||||
for dirpath, dirnames, filenames in os.walk(scan_root):
|
||||
rel = os.path.relpath(dirpath, scan_root)
|
||||
if rel == ".":
|
||||
rel = ""
|
||||
for d in dirnames:
|
||||
if d.startswith(".") or d == "__pycache__":
|
||||
continue
|
||||
existing_ids.add(d)
|
||||
if rel:
|
||||
existing_ids.add(f"{rel}/{d}")
|
||||
for f in filenames:
|
||||
if f.startswith("."):
|
||||
continue
|
||||
stem = os.path.splitext(f)[0]
|
||||
existing_ids.add(stem)
|
||||
existing_ids.add(f)
|
||||
if rel:
|
||||
existing_ids.add(f"{rel}/{f}")
|
||||
existing_ids.add(f"{rel}/{stem}")
|
||||
|
||||
# Common English hyphenated words to skip
|
||||
STOP = {
|
||||
"free-tier", "on-call", "one-line", "one-sentence", "in-flight", "low-risk",
|
||||
"non-zero", "non-empty", "user-facing", "user-visible", "end-to-end",
|
||||
"time-boxed", "long-task", "re-run", "re-scan", "back-to-back", "built-in",
|
||||
"high-value", "open-plan", "night-owls", "self-hosted", "real-world",
|
||||
"long-running", "large-scale", "real-time", "evidence-based", "evidence-backed",
|
||||
"deep-work", "single-day", "known-good", "last-known", "mean-time",
|
||||
"flash-class", "open-weight", "free-llm", "relay-only", "browser-relay",
|
||||
"same-page", "full-time", "part-time", "cross-functional", "cross-tool",
|
||||
"cross-platform", "hard-coded", "hard-wired", "real-estate", "all-in",
|
||||
"out-of-the-box", "out-of-band", "fine-grained", "real-life",
|
||||
"best-practice", "best-in-class", "first-class", "next-gen",
|
||||
"next-generation", "world-class", "cutting-edge", "bleeding-edge",
|
||||
"production-ready", "enterprise-grade", "battle-tested", "cloud-native",
|
||||
"data-driven", "insight-driven", "value-driven", "result-driven",
|
||||
"goal-oriented", "task-oriented", "outcome-oriented", "solution-oriented",
|
||||
"customer-centric", "product-centric", "design-centric", "tech-centric",
|
||||
"human-readable", "machine-readable", "human-friendly", "user-friendly",
|
||||
"cloud-based", "web-based", "api-based", "service-based",
|
||||
"microservice-based", "serverless-based", "container-based",
|
||||
"full-stack", "front-end", "back-end", "middleware",
|
||||
"end-user", "power-user", "super-user", "admin-user",
|
||||
"check-in", "check-out", "log-in", "log-out", "sign-up", "sign-in",
|
||||
"opt-in", "opt-out", "drop-in", "fall-back", "set-up",
|
||||
"follow-up", "catch-up", "keep-up", "give-away", "give-back",
|
||||
"break-down", "break-through", "break-up",
|
||||
"work-around", "work-through", "work-out", "work-up",
|
||||
"think-through", "think-out", "figure-out", "point-out",
|
||||
"carry-out", "carry-on", "carry-over", "carry-through",
|
||||
"come-up", "come-through", "come-back", "come-down",
|
||||
"run-down", "run-through", "run-up", "run-off",
|
||||
"turn-around", "turn-away", "turn-out", "turn-up",
|
||||
"look-up", "look-out", "look-over", "look-through",
|
||||
"make-up", "make-out", "make-over",
|
||||
"put-together", "put-up", "put-down", "put-off",
|
||||
"get-together", "get-up", "get-out", "get-off", "get-on", "get-in",
|
||||
"take-out", "take-up", "take-down", "take-off", "take-on", "take-in",
|
||||
"hand-off", "hand-over", "hand-out", "hand-up",
|
||||
"stand-up", "stand-out", "stand-by", "stand-off",
|
||||
"show-up", "show-off",
|
||||
"fall-back", "fall-out", "fall-through", "fall-off",
|
||||
"call-out", "call-up", "call-off", "call-in",
|
||||
"drop-off", "drop-out", "drop-by", "drop-in",
|
||||
"pass-through", "pass-out", "pass-up", "pass-off",
|
||||
"fail-safe", "fail-over", "fail-fast", "soft-landing",
|
||||
"hard-landing", "cold-start", "warm-up", "cool-down",
|
||||
"heat-up", "speed-up", "slow-down", "scale-up", "scale-down",
|
||||
"grow-up", "grow-out", "grow-in",
|
||||
"roll-out", "roll-in", "roll-up", "roll-down",
|
||||
"spin-up", "spin-down", "spin-off", "spin-out",
|
||||
"boot-up", "boot-down", "shut-down", "power-up", "power-down",
|
||||
"fire-up", "fire-off", "blow-up", "blow-out", "blow-over",
|
||||
"clean-up", "clean-out", "clean-off", "clean-down",
|
||||
"clear-out", "clear-up", "clear-off",
|
||||
"fill-in", "fill-out", "fill-up",
|
||||
"top-up", "top-down", "bottom-up",
|
||||
"up-to-date", "up-to-speed", "up-to-scratch",
|
||||
"out-of-date", "out-of-order", "out-of-control",
|
||||
"out-of-scope", "in-scope", "in-line", "in-place", "in-order",
|
||||
"in-turn", "in-effect", "in-force", "in-action",
|
||||
"on-board", "off-board", "off-site", "on-site",
|
||||
"on-prem", "off-prem", "off-grid", "on-grid",
|
||||
"at-scale", "at-risk", "at-large", "at-hand",
|
||||
"by-default", "by-design", "by-force", "by-law",
|
||||
"for-free", "for-sale", "for-rent", "for-keep",
|
||||
"from-scratch", "from-source", "from-home",
|
||||
"into-action", "into-effect", "into-place",
|
||||
"near-term", "near-future", "far-future", "far-reaching",
|
||||
"high-level", "low-level", "mid-term", "mid-range", "mid-point",
|
||||
"long-term", "short-term", "long-run", "short-run",
|
||||
"raw-data", "raw-feed", "raw-output",
|
||||
"small-scale", "large-scale", "small-size", "large-size",
|
||||
"full-size", "half-size", "double-size", "triple-size",
|
||||
"single-source", "single-point", "single-page", "single-line",
|
||||
"multi-source", "multi-point", "multi-page", "multi-line",
|
||||
"cross-reference", "cross-check", "cross-post", "cross-link",
|
||||
"peer-to-peer", "point-to-point",
|
||||
"many-to-many", "one-to-one", "one-to-many", "many-to-one",
|
||||
"all-or-nothing", "more-or-less", "less-is-more",
|
||||
"more-than", "less-than", "greater-than", "equal-to",
|
||||
"first-come", "first-served", "first-rate",
|
||||
"second-class", "third-class", "top-class", "top-notch",
|
||||
"world-wide", "country-wide", "city-wide",
|
||||
"company-wide", "team-wide", "department-wide",
|
||||
"industry-wide", "market-wide", "region-wide", "global-scale",
|
||||
"local-scale", "regional-scale", "national-scale",
|
||||
"front-line", "back-line", "side-line", "end-line",
|
||||
"hard-line", "soft-line", "bottom-line", "top-line",
|
||||
"red-line", "blue-line", "green-line", "yellow-line",
|
||||
"fine-line", "thin-line", "thick-line",
|
||||
"cut-line", "dotted-line", "dash-line",
|
||||
"head-line", "sub-head", "tag-line", "by-line",
|
||||
"dead-line", "life-line", "time-line",
|
||||
"story-line", "plot-line", "theme-line",
|
||||
"birth-date", "death-date", "due-date", "start-date", "end-date",
|
||||
"launch-date", "release-date", "update-date", "create-date",
|
||||
"modify-date", "change-date", "check-date", "test-date",
|
||||
"run-date", "build-date", "deploy-date", "ship-date",
|
||||
"close-date", "open-date", "book-date", "cancel-date",
|
||||
"plan-date", "review-date", "approve-date", "reject-date",
|
||||
"post-date", "pre-date", "re-date",
|
||||
"double-check", "triple-check",
|
||||
"fact-check", "sanity-check", "reality-check",
|
||||
"health-check", "sense-check", "smoke-check",
|
||||
"quick-check", "deep-check", "full-check", "partial-check",
|
||||
"self-check", "peer-check", "auto-check", "manual-check",
|
||||
"system-check", "unit-check", "integration-check", "e2e-check",
|
||||
"acceptance-check", "compliance-check", "security-check",
|
||||
"performance-check", "load-check", "stress-check",
|
||||
"regression-check",
|
||||
"pre-check", "post-check", "mid-check",
|
||||
"code-check", "style-check", "lint-check", "format-check",
|
||||
"type-check", "logic-check", "data-check", "input-check",
|
||||
"output-check", "config-check", "env-check", "path-check",
|
||||
"file-check", "dir-check", "folder-check", "link-check",
|
||||
"url-check", "reference-check", "cite-check",
|
||||
"source-check", "evidence-check", "proof-check",
|
||||
"sign-check", "crc-check", "hash-check", "checksum-check",
|
||||
"backup-check", "restore-check", "migration-check",
|
||||
"upgrade-check", "downgrade-check", "patch-check",
|
||||
"rollback-check", "failover-check", "recovery-check",
|
||||
"repair-check", "fix-check", "update-check", "refresh-check",
|
||||
"re-index", "re-load", "re-read", "re-write", "re-do", "re-make",
|
||||
"re-build", "re-deploy", "re-start", "re-boot",
|
||||
"re-connect", "re-link", "re-attach", "re-join",
|
||||
"re-enter", "re-import", "re-export", "re-sort", "re-filter",
|
||||
"re-parse", "re-encode", "re-decode", "re-paint", "re-draw",
|
||||
"re-render", "re-size", "re-scale", "re-shape", "re-form",
|
||||
"re-format", "re-struct", "re-arch", "re-design", "re-model",
|
||||
"re-train", "re-fit", "re-predict", "re-infer",
|
||||
"re-query", "re-fetch", "re-call", "re-invoke",
|
||||
"re-trigger", "re-fire", "re-kick", "re-push",
|
||||
"re-pull", "re-merge", "re-base", "re-commit", "re-tag",
|
||||
"re-branch", "re-checkout", "re-clone", "re-fork", "re-spawn",
|
||||
"re-launch", "re-run",
|
||||
}
|
||||
|
||||
ID_RE = re.compile(r"\b[a-z0-9]+(?:-[a-z0-9]+)+\b")
|
||||
BACKTICK_RE = re.compile(r"`([^`]+)`")
|
||||
PATH_IN_TEXT_RE = re.compile(r"(?:^|[\s(])((?:\.{1,2}/|/root/)[^\s)`]+)")
|
||||
REL_PATH_RE = re.compile(r"(?:^|[\s(])((?:wizard-work|wizard-outputs)/[^\s)`]+)")
|
||||
REGEX_META_RE = re.compile(r'[\[\]\(\)\{\}\+\*\?\\\^\$\.\|]')
|
||||
|
||||
def token_in_ref_context(line, start, end):
|
||||
"""True if the token is inside backticks, a path, or a list/table cell."""
|
||||
for m in BACKTICK_RE.finditer(line):
|
||||
if start >= m.start(1) and end <= m.end(1):
|
||||
return True
|
||||
for pm in PATH_IN_TEXT_RE.finditer(line):
|
||||
if start >= pm.start(1) and end <= pm.end(1):
|
||||
return True
|
||||
for pm in REL_PATH_RE.finditer(line):
|
||||
if start >= pm.start(1) and end <= pm.end(1):
|
||||
return True
|
||||
stripped = line.strip()
|
||||
if stripped.startswith(("-", "*", "|")):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_regex_like(token, line, start, end):
|
||||
"""True if the token is inside backticks and looks like a regex/pattern."""
|
||||
for m in BACKTICK_RE.finditer(line):
|
||||
if start >= m.start(1) and end <= m.end(1):
|
||||
inner = m.group(1)
|
||||
# extract the token from inside the backticks
|
||||
idx = start - m.start(1)
|
||||
snippet = inner[max(0, idx-5):idx+len(token)+5]
|
||||
if REGEX_META_RE.search(snippet):
|
||||
return True
|
||||
return False
|
||||
|
||||
def scan_text(lines, source_label):
|
||||
violations = []
|
||||
in_code = False
|
||||
for lineno, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("```"):
|
||||
in_code = not in_code
|
||||
continue
|
||||
if in_code:
|
||||
continue
|
||||
if "[PLACEHOLDER:" in line or "<PLACEHOLDER:" in line:
|
||||
continue
|
||||
for m in ID_RE.finditer(line):
|
||||
token = m.group(0)
|
||||
if token.lower() in STOP:
|
||||
continue
|
||||
start, end = m.span(0)
|
||||
if "<" in line[max(0, start-2):start] or ">" in line[end:end+2]:
|
||||
continue
|
||||
if not token_in_ref_context(line, start, end):
|
||||
continue
|
||||
if is_regex_like(token, line, start, end):
|
||||
continue
|
||||
if token not in existing_ids:
|
||||
violations.append((source_label, lineno, token, line.strip()))
|
||||
return violations
|
||||
|
||||
# 1. Scan the standard document itself
|
||||
std_lines = open(STANDARD, "r", encoding="utf-8", errors="ignore").read().splitlines()
|
||||
std_violations = scan_text(std_lines, STANDARD)
|
||||
|
||||
# 2. Embedded unit tests
|
||||
test_cases = [
|
||||
("clean backticked real ID", "See `queue-triage` for details.", 0),
|
||||
("clean backticked placeholder", "See `<fleet-ops>` for details.", 0),
|
||||
("clean backticked placeholder marker", "See `[PLACEHOLDER: fleet-ops]` for details.", 0),
|
||||
("violation bare invented in backtick", "See `fleet-ops` for details.", 1),
|
||||
("violation bare invented in list", "- fleet-ops for details.", 1),
|
||||
("clean English hyphen", "This is a free-tier service.", 0),
|
||||
("clean English hyphen in list", "- free-tier service.", 0),
|
||||
]
|
||||
|
||||
test_violations = []
|
||||
for desc, line, expected in test_cases:
|
||||
result = scan_text([line], f"test:{desc}")
|
||||
actual = len(result)
|
||||
if actual != expected:
|
||||
test_violations.append((desc, expected, actual, result))
|
||||
|
||||
all_violations = std_violations + test_violations
|
||||
|
||||
if all_violations:
|
||||
print(f"VIOLATIONS: {len(all_violations)}")
|
||||
for item in all_violations:
|
||||
if len(item) == 4:
|
||||
src, ln, token, ctx = item
|
||||
print(f" {src}:{ln}: bare ID '{token}' not in repo")
|
||||
print(f" context: {ctx[:120]}")
|
||||
else:
|
||||
desc, exp, act, res = item
|
||||
print(f" TEST FAIL: {desc} (expected {exp}, got {act})")
|
||||
for r in res:
|
||||
print(f" {r}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("PASS: zero bare-ID violations found; all embedded tests passed.")
|
||||
sys.exit(0)
|
||||
Reference in new issue
Block a user