feat(architecture): default map-landscape to this repository and its reference graph - #4057
Conversation
Run /architecture:map-landscape in explicit --repos mode against this checkout and land the two artifacts under docs/architecture, the home the skill's own inference proposes when nothing is declared. Both files carry the generated-on line, the discovery source, and per-fact evidence from portfolio-facts.sh; no relationship line is drawn because a single repository has nothing to name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
Persist the interview contract for reworking the map-landscape skill: a bare run charts the current repository plus its one-hop reference graph, edges are typed and script-extracted, docs/architecture/landscape.json is the committed record with drift reporting, the facts script gains a runtime-versus-development scope axis, and --remote facts are opt-in. Twelve interview questions answered, none deferred; sequencing is one tracking issue with four children. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
…ortfolio facts The facts probe classified this repository as node,python with sixteen dependencies, all of them CI pins, because a requirements file under .github/ matched the Python glob and a devDependencies-only package.json counted as a Node runtime. A repository runs on its runtime and is built with its tooling, and every standard that separates the two draws the line on scope rather than path: CycloneDX scope, SPDX DEV_DEPENDENCY_OF and BUILD_TOOL_OF, npm devDependencies, PEP 735 dependency groups, and the GitHub dependency graph. The record gains tooling and dev_dependencies beside runtime and dependencies, each with its own evidence. target_framework follows the primary runtime only, so an engine constraint for a linter no longer fills it. Cache and build dot-directories are pruned from the manifest index, the CI and container config directories are kept, and every manifest under a dot-directory is pinned to development scope: the path rule decides visibility, the scope axis decides runtime versus tooling. A root-level manifest now beats a deeper one, which previously reported whichever component sorted first. This checkout now reports runtime shell with tooling node,python, and its CI pins as development-scope dependencies citing both manifests. Refs #4034 Refs #4033 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
…cted facts The first run recorded this repository as a node,python runtime with sixteen dependencies, all of them CI tool pins. The scope split corrects that to a shell runtime with node and python tooling and no runtime dependencies, so the committed artifacts are regenerated to match rather than left asserting facts the probe no longer produces. The portfolio table gains the Tooling column and states the scope split it now reports. The landscape names why the nineteen repositories this checkout references are absent from the diagram. Refs #4034 Refs #4033 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
… tracked files A landscape drawn only from locally checked-out repositories shows one node and no edges: the related systems are named by reference, not by adjacency on disk. This checkout names eleven sibling repositories and several third parties, none of which the current skill can draw. reference-edges.sh reads tracked files and emits one JSON record per (target, type) pair with a reference count and the citing files. Four types, each anchored on a syntax that only ever names a repository: uses-workflow (a workflow or composite-action step), installs-plugin (a marketplace source or install line), depends-on (a Go module path), and cites (a github.com URL, or a bare owner/repo whose owner matches this repository's own). The owner requirement on bare tokens is what makes the weakest type usable. A single owner/repo regex over all tracked text is why this was left to the model before: on this repository it matches sponsors/ out of a funding URL, en/ out of a docs.github.com path, and every acme/billing in a fixture. Alongside that, the extractors reject test and eval files, GitHub's own product surfaces, documentation placeholders, local marketplace sources, and self-references folded case-insensitively; ref pins, .git suffixes, and sentence-ending periods are stripped from the repository segment. Against this checkout it finds 77 edges, including 24 uses-workflow references to ci-workflows, matching an independent count of the same workflow lines. Refs #4035 Refs #4033 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
The landscape showed one node because the skill can only relate repositories that are checked out locally, and none of this repository's siblings are. With the edge extractor those references become edges, so the diagram now carries the seven same-owner systems this repository names most, the two most-referenced external ones, and a typed, counted relationship for each. The omissions are stated rather than silently dropped: five same-owner repositories referenced once or twice, and sixty-three further external ones that are tool and documentation citations rather than related systems. Node descriptions are marked as annotations, since the extractor reports that a repository is referenced and how, never what it is for. Refs #4035 Refs #4033 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
A landscape collected fresh on every run has no memory: nothing tells the operator that a system appeared, an edge vanished, or a runtime changed since the last time anyone looked. landscape-record.sh composes the fact and edge collectors into one schema_version 1 record the repository commits, one object per line so review sees a real diff, and compares a fresh collection against a committed one. The drift report names repositories and edges added or removed, fields whose value changed, and cited evidence files that are no longer in the checkout. `path` is excluded from the comparison: it says where a checkout sits on one machine, not what the system is. Exit 3 on drift gives callers a check gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
Table rows, dependency truncation, diagram alias sanitising, boundary grouping and node labels are all mechanical, and rendering them by hand made two runs on identical facts produce different files. render-landscape.sh reads the record and writes landscape.md (mermaid C4Context) or landscape.dsl (Structurizr systemLandscape) plus portfolio.md, doing only work the record decides. Internal systems sort by owner first so each owner forms one contiguous boundary; externals sort by reference count so --top-external keeps the ones that carry the most evidence, and the remainder is counted rather than dropped silently. Prose comes from an annotations file this script only appends. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
…references A bare invocation stopped and asked for a scope argument, and even an explicit one drew a single node, because relationships required the other repository to be a local checkout. A repository already names its neighbours in its workflows, marketplace sources, module paths and docs; the skill now reads them. The default scope is the current repository plus its reference graph, one hop out. --repos and --root stay as explicit overrides, --out redirects one run, and the working directory is still never walked for nested repositories. The record is committed and every re-run compares against it before writing, with --check as the read-only CI shape. --remote is opt-in, presence-gated, and loses to a local checkout for any fact both can supply. The --root collaborator protocol and the --remote contract move to a scope-modes reference spoke: both are rare paths whose detail does not belong in context on a default run. This repository's own artifacts are regenerated through the new pipeline, with the annotations it cannot derive in landscape-notes.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
…-only A landscape that quietly drops archived repositories hides exactly the dependencies worth acting on, so a --remote run charts and marks them. --check gains the instruction it was missing: report the comparison's exit code as the run's outcome and do not refresh the record to make the next run clean. A check lane that fixes what it is checking has stopped being a check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
Three files conflicted. Resolutions: - plugin.json: keep 0.9.0, which is ahead of the 0.8.7 main shipped, with this branch's rewritten landscape description. - CHANGELOG.md: main claimed 0.8.6 for the config.md dialect mapping while this branch had claimed it for the scope axis, so the scope-axis entry moves into 0.9.0 where the rest of this work already sits. Main's 0.8.6 and 0.8.7 entries are kept verbatim. - SKILL.md: take main's mermaid-C4 gotcha, which defers the experimental stamp and recheck trigger to reference/config.md instead of carrying a second dated one here. Every other gotcha on this branch is kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
exec-bit: the two new test scripts carry a shebang without the executable bit, which the repo-wide gate rejects. changed-skills: the render suite read `npx --no-install` exiting non-zero as "the renderer emits bad markdown", when on a host without the package installed it only means the tool is absent. The two are indistinguishable at the exit code, so the check now probes on a file known to be clean first and skips explicitly when that probe fails. contract-slice-diff: docs/topics/ is contract tier, committed on a task branch and pruned before merge. The Brief's durable decisions graduate to docs/adr/0032: charting from a repository's own references, the one-syntax-per edge type rule, runtime versus development as a scope axis, the committed record the artifacts derive from, and other-owner repositories as read-only reference. Its Evidence section carries the standards survey and the concrete false matches that ruled out a naive reference regex. The slice itself is gone; the PR body names the pre-prune commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
… more Adjudicating this branch's open decisions against official sources surfaced six defects. Every design decision was confirmed; these are the bugs found on the way. The serious one: once landscape.json was tracked, the edge extractor read it back as evidence. The record names every repository it charted, so each run raised every citation count by one and listed the record among its own sources. It hid because git grep scans only tracked files, so the first build (with the record still untracked) looked clean and committing it armed the loop. A drift gate that can never report clean is not a gate. This skill's own artifact names are fixed by its contract while only their directory varies, so the extractor now skips them by basename, and regenerating twice is byte-identical. A record built with remote facts carries repositories no local-only run can produce, and every one of them reported as removed. The comparison now checks the recorded posture against the run's own and says when the two sides are not the same kind of thing. Two checkouts sharing a basename collapsed onto one identity and silently matched the wrong row; that is now reported rather than guessed at. A clean comparison carrying non-gating differences no longer says the record "matches" and then lists what moved. A repository with no resolvable owner is drawn outside every boundary in both dialects, because a boundary is captioned with an organisation and `unknown` is the absence of one. The Structurizr External tag gains the styles block that makes it render, since `location` was removed upstream and the tag is the only carrier left for that fact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @kyle-sexton's task in 3m 3s —— View job Working on it
|
|
Claude finished @kyle-sexton's task in 6m 7s —— View job CI security review
One finding, posted inline on IMPORTANT — untrusted manifest content breaks out of the quoted Mermaid/Structurizr string literals it's interpolated into. No other issues met the high-signal bar: the other new/changed scripts ( |
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c971100c26
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Last security-reviewed head: |
… descriptions Both are generated from the plugin manifest and the skill frontmatter, and this branch rewrote the plugin description and the skill summary without rerunning the generators. The drift check that catches it runs only in the full test lanes, which a draft pull request skips, so it stayed invisible until the flip to ready armed those lanes. The catalog was the reported failure on both test-linux shards. Fixing it surfaced the cheat sheet behind it: the validator stops at the first drift, so one stale generated file masks the next. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
Repository-controlled text could break out of the string literal it was written into. A target framework is read out of a manifest with only XML tags stripped, and a raw quote is legal there, so a crafted value could splice arbitrary syntax into a committed diagram. Values are now decoded out of the record and their delimiters replaced for the target grammar, and a pipe is escaped before it reaches a portfolio-table cell. Two names differing only in punctuation collapsed onto one diagram identifier, so both dialects declared the system twice and pointed every relationship at whichever declaration won. Aliases are handed out once, in emission order, and a repeat gets a counted suffix. A quoted `uses:` scalar left its opening quote on the owner segment, which failed the character check and dropped the edge without a word. A checkout on disk was read as a claim of ownership: every locally collected repository was internal whatever its owner, so a third-party clone sat inside an enterprise boundary while the edges to it said external. The record now names its `subject_owner`, resolved by the edge extractor so the nodes and the edges cannot disagree, and a cross-owner checkout renders external with its probed facts intact. It is drawn whatever `--top-external` says, because that cap trims the tail of repositories a run only read about, not the set someone asked to chart. `--remote` recorded only a status string, so fetched facts had nowhere to land and every referenced repository stayed factless however much was fetched. `--remote-facts <file>` merges them, a local checkout winning outright over an entry of the same name, and an archived repository is marked in the node description and the portfolio row as the remote-facts contract already promised. The committed landscape.md was also stale against its own record: it drew two external systems where the record has five. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i
…ap-landscape-3fuoh1
Integrating main brought in tracked files that the reference graph reads, so `anthropics/skills` gains two citations. The `stale-base-overlap` gate refused the branch for the same reason it refuses any stale base: both sides had touched docs/CATALOG.md, and a squash cut from there can revert what landed on the base after the branch point. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i

Closes #4033
Closes #4034
Closes #4035
Closes #4036
Closes #4037
Summary
/architecture:map-landscapestopped and asked for a scope argument on a bareinvocation, and even with
--repos <this checkout>it drew one node with noedges, because a relationship required the other repository to be a local
checkout. On a cloud checkout of this repository that is the whole answer: one
box, despite the tracked files naming nineteen other organisation repositories.
A repository already says which systems it builds against, in its workflows,
marketplace sources, module paths and docs. This reworks the skill to read them,
commit the result, and report what moved on the next run.
Running it on this repository now yields eleven organisation systems and
sixty-five external ones, with 24
uses-workflowreferences toci-workflowsand 54
citestostandards.Reviewing this PR: the commits are the intended review unit and segment along
the child issues in order. Reading commit by commit gives the same incremental
progression a stacked PR would, without the merge ordering.
13710207,8eb390bc2cb2c1ad,3bc25b12a5ef14b9,e2de646a,35465ae88f55c30cb1c125d0,4ce96d02c9a71394,9e3f926d,c971100c,9315a8157cbb9b2dFix
Scope hygiene in the facts probe (#4034).
portfolio-facts.shseparatesruntime scope from development scope on the axis every surveyed standard uses
(CycloneDX
scope, SPDX dependency relationships, npmdevDependencies, PEP 735dependency groups, the GitHub dependency-submission API). The record gains
toolinganddev_dependenciesbesideruntimeanddependencies, each withits own evidence. Cache and build dot-directories are pruned; the CI and
container config directories are kept, and every manifest under one is pinned to
development scope whatever its content says. This checkout reports
runtime: shellwithtooling: node, pythonrather than claiming a Pythonruntime from its CI pins.
Typed reference edges (#4035).
reference-edges.shreads one repository'stracked files and emits typed, counted edges. Each type trusts exactly one
syntax:
uses-workflowa workflowuses:step,installs-plugina marketplacesource,
depends-ona module path,citesa github.com URL or a bareowner/repowhose owner matches the subject's own. A singleowner/reporegexover all tracked text was rejected because on this checkout it matches
sponsors/,en/out of a docs URL, and every fixture in every test file.Default scope and the committed record (#4035). A bare invocation charts the
current repository plus its reference graph, one hop out.
--reposand--rootstay as explicit overrides;
--outredirects one run.landscape-record.shassembles both collectors into a
schema_version1landscape.json, one objectper line so review sees a real diff, and compares a fresh collection against the
committed one.
--checkruns the comparison, writes nothing, and exits non-zero.Rendering (#4036).
render-landscape.shwriteslandscape.md(mermaidC4Context) orlandscape.dsl(StructurizrsystemLandscape) plusportfolio.mdfrom the record, doing only work the record decides. Internalsystems sort by owner first so each owner forms one contiguous boundary;
externals sort by reference count, and
--top-externalkeeps the ones carryingthe most evidence while counting the remainder rather than dropping it silently.
Opt-in remote facts (#4037).
--remotefills facts for referencedrepositories that are not checked out here. Off by default, presence-gated on the
GitHub MCP tools or an authenticated
gh, degrading to local-only with themissing backend named. A local checkout always wins for a fact both sources
carry. Fetching itself is not scripted: these are MCP calls the model makes, and
only reasoning-free work is scripted. What the fetch produces reaches the record
through
landscape-record.sh --remote-facts <file>.Decisions, adjudicated against official sources
Five open decisions were researched against official specifications and then
resolved by independent fresh-context agents that were not shown the
implementer's reasoning. All five confirmed the implementation; the research
corrected two claims this PR body previously made, and the adjudication surfaced
six defects, fixed in
c971100c.pathexcluded from the recordlast_touchednon-gatingSKILL.mdlengthTwo corrections to earlier claims in this body:
The "217 lines against a soft target of 200" framing was wrong. No 200-line
figure exists in any Anthropic source. The docs, the engineering blog, the
anthropics/skillsREADME and all 41 bundled skills were checked. The onlytraceable origin is a third-party community wiki. Anthropic's own
skill-creatoris 485 lines, its validator enforces no line check, and thefirst-party playbook says the counts are "approximate and you can feel free to
go longer if needed." This is not a gap; the local threshold has no upstream
basis, and 90 of this repo's 261 skills breach it.
An undisclosed deviation from architecture/map-landscape: render both landscape dialects and the portfolio table from landscape.json with a tested script #4036, now disclosed. That issue specified
"one external boundary". This PR does not implement it: externals render as
top-level
System_Extoutside every boundary. The instruction has zerosupport in any authoritative source. Across 24 c4model.com pages, the
Structurizr docs and cookbook, the Mermaid docs, and the C4-PlantUML README
there is no boundary labelled "external"; every organisational boundary in
every official example names a real organisation. "External" is a relation, not
an organisation, and both dialects already carry it on the element. The
recommendation is to close that part of architecture/map-landscape: render both landscape dialects and the portfolio table from landscape.json with a tested script #4036 as unsupported. The defect here
was silence, not the code.
Review round: five findings, all real, all fixed in
7cbb9b2dThe flip to ready drew a Codex review and a Claude security review. Every
finding was verified against the code before acting; none was rejected.
(security review,
IMPORTANT). A target framework is read out of<TargetFrameworks>with only XML tags stripped, and a raw"is legal,unescaped XML element text. The renderer wrote it straight into a quoted
System(...)orsoftwareSystemstring, so a crafted.csprojcould splicearbitrary diagram syntax, a live
clickdirective included, into a committedartifact. The root cause was
unquote()stripping the JSON delimiters withoutundoing
\"/\\. Values are now decoded out of the record, and theirdelimiters replaced for the target grammar at the one point the model rows are
emitted. Replaced rather than escaped: neither dialect offers an escape for
its own delimiter that could be verified as portable, and a quote in a
repository name, a CODEOWNERS owner or a target framework is corrupt data, not
a fact worth preserving byte for byte. The same class in the portfolio table
is closed too: a
|ended its cell and shifted every column after it.(Codex, P2).
a-banda_bare both legal GitHub names and can sit in oneorg. Both dialects declared the system twice and pointed every relationship at
whichever declaration won. Aliases are now handed out once, in emission order,
with a counted suffix on a repeat.
uses:scalar dropped its edge in silence (Codex, P2). Theopening quote stayed on the owner segment, failed the character check, and the
edge vanished with no diagnostic, so any repository writing ordinary quoted
YAML charted an incomplete graph.
locally collected repository was internal whatever its owner, so a third-party
clone sat inside an enterprise boundary while the edges to it said external.
This is the internal-side twin of the
unknownboundary caption fixed inc971100c. The record now names itssubject_owner, resolved by the edgeextractor through a new
--print-ownerso the nodes and the edges cannotdisagree about who the subject is. A probed repository is drawn whatever
--top-externalsays, because that cap trims the tail of repositories a runonly read about, not the set someone asked it to chart.
--remotecould not produce its promised output (Codex, P1). It recordeda status string and nothing else; no command accepted the fetched values, so
every referenced repository stayed factless however much was fetched, and an
archived repository could not be marked as
scope-modes.mdpromises.landscape-record.sh --remote-facts <file>now merges them, before bothemission and comparison so a merged record compares clean against itself. A
local checkout wins outright rather than field by field: a probe that read the
files is a better witness than an API summary of them, and a row assembled
from both is one no single source stands behind.
archivedleads the nodedescription and marks the portfolio row;
default_branchandvisibilityarecarried in the record and
scope-modes.mdnow says so rather than leaving thereader to infer it.
Two things found while fixing rather than while reviewing:
landscape.mdwas stale against its own record: it drew twoexternal systems where the record has five. The pre-fix renderer reproduces
five from the same record, so this was a missed regeneration, not a renderer
bug. Corrected.
uses:test passed against the unfixed script. Asame-owner bare
owner/repotoken in the same file is also aciteshit, soasserting the target alone was satisfied by the citation while the
uses-workflowedge stayed missing. The assertions now match on type as well.Verification
landscape-record.test.shrender-landscape.test.shreference-edges.test.shportfolio-facts.test.shshellcheck --rcfile .shellcheckrcshfmt -i 2markdownlint-cli2check-skill.sh map-landscapescripts/validate-plugins.shSix defects found during the decision adjudication, fixed in
c971100c:landscape.jsonwas tracked, the extractorread it back as evidence. It names every repository it charted, so each run
raised every citation count by one and listed the record among its own
sources. It hid because
git grepscans only tracked files, so the firstbuild (record still untracked) looked clean and committing it armed the loop.
A drift gate that can never report clean is not a gate. The extractor now
skips this skill's own artifacts by basename, and the record is a fixed point.
--remotecarried repositories no local-only run canproduce; every one reported as
removed repository, which gates. Thecomparison now checks the recorded posture against the run's own.
matched the wrong row. Now reported as ambiguous.
"matches" and then listed what moved.
unknown, the same category error as the "external" boundary, from theinternal side. Now drawn outside every boundary in both dialects.
Externaltag was inert with nostylesblock, so the DSLartifact silently lost a distinction the mermaid one keeps.
One pre-existing failure, not this branch's:
plugins/claude-ops/skills/plugins/scripts/cache-content-check.test.shfails 2of 26 on its process-budget probe, reproducibly, on a tree with none of this
branch's changes applied. It passes in CI. This diff touches no file under
plugins/claude-ops/.Related
docs/topics/map-landscape-redesign/wascontract tier and is pruned per
docs/conventions/topic-docs/README.md. Thedurable decisions graduated to
docs/adr/0032-chart-a-landscape-from-references-and-commit-it-as-a-record.md.Pre-prune commit SHA:
c9a71394c2e2e829e0e7f16bf9fd8f64f1a504b8. The Brief ispasted below so it survives the prune.
LINE_SOFT_CAP=200inplugins/skill-quality/scripts/check-skill.shis a bare assignment with norecorded derivation, sitting under the traceable
LINE_HARD_CAP=500. With 90of 261 skills breaching it, it emits a standing warning authors learn to read
past, which erodes the other 24 checks in the same script. It deserves a
derivation comment, a raise, or retirement.
consent in advance rather than disclosure after the fact. Consent to take this
as one unit covering five issues was given before the flip to ready.
Pruned contract:
docs/topics/map-landscape-redesign/PLAN.mdTLDR
/architecture:map-landscapecharts the current repository plus its reference graph, onehop out;
--reposand--rootstay as explicit overrides.repositories render as external systems, kept read-only.
docs/architecture/landscape.json; both rendered artifacts derive from itthrough a reasoning-free render script, and every re-run reports drift, with
--checkfor CI..github/and otherdot-directories from runtime probes, and adds a Tooling column.
--remotefacts are opt-in and presence-gated; a fixed closing report and a rewritten descriptionmake single-repository-plus-references the primary use.
Goal
Rework the
map-landscapeskill so that one run in one repository yields a useful, committed,re-runnable landscape of that repository and everything it references. Today the skill only reads
local checkouts and stops on a bare invocation, so on a cloud checkout of this repository it drew one
node with no edges even though the tracked files name nineteen other organisation repositories.
Constraints
do only reasoning-free work (tables, alias sanitising, boundary grouping, labels).
plugins/architecture/reference/config.mdstays: an interactive runwith no home proposes
docs/architecture,--out <dir>overrides one run, and a non-interactive runwith neither still stops.
--remoteis passed. Externals are never fetched from unless--remote=all, and nothing ever writes to another repository.portfolio-facts.sh; no jq, no python..claude/rules/skill-bodies-state-current-rules.md; validation runs throughscripts/affected-tests.sh --run.Acceptance criteria
ci-workflowsandstandardsas nodes andtyped edges to them, without any argument.
--remoteis absent, THEN no network call is made.docs/architecture/landscape.jsonexists, a re-run reports drift instead ofsilently overwriting, and
--checkexits non-zero on drift.no-scope-names-both-formseval is replaced by one assertingthe bare default.
Captured assumptions
ghis available when--remoteis used; otherwise the flagreports unavailability and continues local-only. Revisit if a consumer needs remote facts with
neither.
originowner segment. Revisit if a consumerhas repositories split across owners it considers one enterprise.
ci_toolingbucket was rejected in favour of a scope axis because every surveyedstandard (CycloneDX scope, SPDX dependency relationships, npm devDependencies, PEP 735 dependency
groups, GitHub dependency-graph scope) separates runtime from development on scope, and GitHub
Linguist vendors
.github/out of language statistics. Revisit if a manifest family appears whosescope cannot be read from the file.
Out-of-scope
--remote./repo-fleet-hygiene:audit), GitHub organisation settings(
/github:audit), doc-versus-code drift inside one repository (/codebase-health:audit), andmodule-level structure (
/architecture:improve)./architecture:setup applyowns that.Deferred questions
None. All twelve interview questions were answered; see the memory-tier ledger.
Sequencing
One tracking issue with four children, in order, each shipped as its own draft PR:
portfolio-facts.sh(scope axis, dot-directory pruning, Tooling).landscape.jsonrecord with drift and--check, closing report, description and README rewrite, evals.--remotefacts.🤖 Generated with Claude Code
https://claude.ai/code/session_012Drcna7MoR2BX9KrLqRy5i